d5763326b1fff449585a21a7cf8a6bc590f0294a
[nit.git] / lib / socket / socket_c.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Low-level socket functionalities
18 module socket_c
19
20 in "C Header" `{
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <sys/socket.h>
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <netdb.h>
30 #include <sys/poll.h>
31 #include <errno.h>
32
33 typedef int S_DESCRIPTOR;
34 typedef struct sockaddr_in S_ADDR_IN;
35 typedef struct sockaddr S_ADDR;
36 typedef struct in_addr S_IN_ADDR;
37 typedef struct hostent S_HOSTENT;
38 typedef struct timeval S_TIMEVAL;
39 typedef struct sockaccept_result { S_ADDR_IN addr_in; S_DESCRIPTOR s_desc; } S_ACCEPT_RESULT;
40 typedef fd_set S_FD_SET;
41 typedef socklen_t S_LEN;
42 `}
43
44 # Wrapper for the data structure PollFD used for polling on a socket
45 class PollFD
46
47 # The PollFD object
48 private var poll_struct: FFSocketPollFD
49
50 # A collection of the events to be watched
51 var events: Array[FFSocketPollValues]
52
53 init(pid: Int, events: Array[FFSocketPollValues])
54 do
55 assert events.length >= 1
56 self.events = events
57
58 var events_in_one = events[0]
59
60 for i in [1 .. events.length-1] do
61 events_in_one += events[i]
62 end
63
64 self.poll_struct = new FFSocketPollFD(pid, events_in_one)
65 end
66
67 # Reads the response and returns an array with the type of events that have been found
68 private fun check_response(response: Int): Array[FFSocketPollValues]
69 do
70 var resp_array = new Array[FFSocketPollValues]
71 for i in events do
72 if c_check_resp(response, i) != 0 then
73 resp_array.push(i)
74 end
75 end
76 return resp_array
77 end
78
79 # Checks if the poll call has returned true for a particular type of event
80 private fun c_check_resp(response: Int, mask: FFSocketPollValues): Int
81 `{
82 return response & mask;
83 `}
84
85 end
86
87 # Data structure used by the poll function
88 private extern FFSocketPollFD `{ struct pollfd `}
89 # File descriptor id
90 private fun fd: Int `{ return recv.fd; `}
91 # List of events to be watched
92 private fun events: Int `{ return recv.events; `}
93 # List of events received by the last poll function
94 private fun revents: Int `{ return recv.revents; `}
95
96 new (pid: Int, events: FFSocketPollValues) `{
97 struct pollfd poll;
98 poll.fd = pid;
99 poll.events = events;
100 return poll;
101 `}
102
103 end
104
105 extern FFSocket `{ S_DESCRIPTOR* `}
106 new socket(domain: FFSocketAddressFamilies, socketType: FFSocketTypes, protocol: FFSocketProtocolFamilies) `{
107 S_DESCRIPTOR *d = NULL; d = (S_DESCRIPTOR*) malloc( sizeof(S_DESCRIPTOR) );
108 int ds = socket(domain, socketType, protocol);
109 if(ds == -1){
110 free(d);
111 return NULL;
112 }
113 memcpy(d, &ds, sizeof(ds));
114 return d;
115 `}
116 fun destroy `{ free(recv); `}
117 fun close: Int `{ return close( *recv ); `}
118 fun descriptor: Int `{ return *recv; `}
119 fun errno: Int `{ return errno; `}
120
121 fun gethostbyname(n: String): FFSocketHostent import String.to_cstring `{ return gethostbyname(String_to_cstring(n)); `}
122 fun connect(addrIn: FFSocketAddrIn): Int `{ return connect( *recv, (S_ADDR*)addrIn, sizeof(*addrIn) ); `}
123 fun write(buffer: String): Int import String.to_cstring, String.length `{ return write(*recv, (char*)String_to_cstring(buffer), String_length(buffer)); `}
124
125 fun read: String import NativeString.to_s_with_length `{
126 static char c[1024];
127 int n = read(*recv, c, 1024);
128 if(n < 0) {
129 free(c);
130 return NativeString_to_s_with_length("",0);
131 }
132 char* ret = malloc(n + 1);
133 memcpy(ret, c, n);
134 ret[n] = '\0';
135 free(c);
136 return NativeString_to_s_with_length(ret, n);
137 `}
138
139 # Sets an option for the socket
140 fun setsockopt(level: FFSocketOptLevels, option_name: FFSocketOptNames, option_value: Int) `{
141 int err = setsockopt(*recv, level, option_name, &option_value, sizeof(int));
142 if(err != 0){
143 perror("Error on setsockopts : ");
144 exit(1);
145 }
146 `}
147
148 fun bind(addrIn: FFSocketAddrIn): Int `{ return bind(*recv, (S_ADDR*)addrIn, sizeof(*addrIn)); `}
149 fun listen(size: Int): Int `{ return listen(*recv, size); `}
150
151 # Checks if the buffer is ready for any event specified when creating the pollfd structure
152 fun socket_poll(filedesc: PollFD, timeout: Int): Array[FFSocketPollValues]
153 do
154 var result = i_poll(filedesc.poll_struct, timeout)
155 assert result != -1
156 return filedesc.check_response(result)
157 end
158
159 # Call to the poll function of the C socket
160 #
161 # Signature :
162 # int poll(struct pollfd fds[], nfds_t nfds, int timeout);
163 #
164 # Official documentation of the poll function :
165 #
166 # The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.
167 # For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events.
168 # The number of pollfd structures in the fds array is specified by nfds.
169 # The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred.
170 # The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor.
171 # It is a pointer to an array with one member for each open file descriptor of interest.
172 # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
173 # OR'ing a combination of the pollfd flags.
174 private fun i_poll(filedesc: FFSocketPollFD, timeout: Int): Int `{
175 int poll_return = poll(&filedesc, 1, timeout);
176 return poll_return;
177 `}
178
179 private fun i_accept(addrIn: FFSocketAddrIn): FFSocket `{
180 S_LEN s = sizeof(S_ADDR);
181 S_DESCRIPTOR *d = NULL;
182 d = malloc(sizeof(S_DESCRIPTOR));
183 *d = accept(*recv,(S_ADDR*)addrIn, &s);
184 return d;
185 `}
186 fun accept: FFSocketAcceptResult
187 do
188 var addrIn = new FFSocketAddrIn
189 var s = i_accept(addrIn)
190 return new FFSocketAcceptResult(s, addrIn)
191 end
192 end
193
194 extern FFSocketAcceptResult `{ S_ACCEPT_RESULT* `}
195 new (socket: FFSocket, addrIn: FFSocketAddrIn) `{
196 S_ACCEPT_RESULT *sar = NULL;
197 sar = malloc( sizeof(S_ACCEPT_RESULT) );
198 sar->s_desc = *socket;
199 sar->addr_in = *addrIn;
200 return sar;
201 `}
202 fun socket: FFSocket `{ return &recv->s_desc; `}
203 fun addrIn: FFSocketAddrIn `{ return &recv->addr_in; `}
204 fun destroy `{ free(recv); `}
205 end
206
207 extern FFSocketAddrIn `{ S_ADDR_IN* `}
208 new `{
209 S_ADDR_IN *sai = NULL;
210 sai = malloc( sizeof(S_ADDR_IN) );
211 return sai;
212 `}
213 new with(port: Int, family: FFSocketAddressFamilies) `{
214 S_ADDR_IN *sai = NULL;
215 sai = malloc( sizeof(S_ADDR_IN) );
216 sai->sin_family = family;
217 sai->sin_port = htons(port);
218 sai->sin_addr.s_addr = INADDR_ANY;
219 return sai;
220 `}
221 new with_hostent(hostent: FFSocketHostent, port: Int) `{
222 S_ADDR_IN *sai = NULL;
223 sai = malloc( sizeof(S_ADDR_IN) );
224 sai->sin_family = hostent->h_addrtype;
225 sai->sin_port = htons(port);
226 memcpy( (char*)&sai->sin_addr.s_addr, (char*)hostent->h_addr, hostent->h_length );
227 return sai;
228 `}
229 fun address: String import NativeString.to_s `{ return NativeString_to_s( (char*)inet_ntoa(recv->sin_addr) ); `}
230 fun family: FFSocketAddressFamilies `{ return recv->sin_family; `}
231 fun port: Int `{ return ntohs(recv->sin_port); `}
232 fun destroy `{ free(recv); `}
233 end
234
235 extern FFSocketHostent `{ S_HOSTENT* `}
236 private fun i_h_aliases(i: Int): String import NativeString.to_s `{ return NativeString_to_s(recv->h_aliases[i]); `}
237 private fun i_h_aliases_reachable(i: Int): Bool `{ return (recv->h_aliases[i] != NULL); `}
238 fun h_aliases: Array[String]
239 do
240 var i=0
241 var d=new Array[String]
242 loop
243 d.add(i_h_aliases(i))
244 if i_h_aliases_reachable(i+1) == false then break
245 i += 1
246 end
247 return d
248 end
249 fun h_addr: String import NativeString.to_s `{ return NativeString_to_s( (char*)inet_ntoa(*(S_IN_ADDR*)recv->h_addr) ); `}
250 fun h_addrtype: Int `{ return recv->h_addrtype; `}
251 fun h_length: Int `{ return recv->h_length; `}
252 fun h_name: String import NativeString.to_s `{ return NativeString_to_s(recv->h_name); `}
253 end
254
255 extern FFTimeval `{ S_TIMEVAL* `}
256 new (seconds: Int, microseconds: Int) `{
257 S_TIMEVAL* tv = NULL;
258 tv = malloc( sizeof(S_TIMEVAL) );
259 tv->tv_sec = seconds;
260 tv->tv_usec = microseconds;
261 return tv;
262 `}
263 fun seconds: Int `{ return recv->tv_sec; `}
264 fun microseconds: Int `{ return recv->tv_usec; `}
265 fun destroy `{ free( recv ); `}
266 end
267
268 extern FFSocketSet `{ S_FD_SET* `}
269 new `{
270 S_FD_SET *f = NULL;
271 f = malloc( sizeof(S_FD_SET) );
272 return f;
273 `}
274 fun set(s: FFSocket) `{ FD_SET( *s, recv ); `}
275 fun is_set(s: FFSocket): Bool `{ return FD_ISSET( *s, recv ); `}
276 fun zero `{ FD_ZERO( recv ); `}
277 fun clear(s: FFSocket) `{ FD_CLR( *s, recv ); `}
278 fun destroy `{ free( recv ); `}
279 end
280
281 class FFSocketObserver
282 fun select(max: FFSocket, reads: nullable FFSocketSet, write: nullable FFSocketSet,
283 except: nullable FFSocketSet, timeout: FFTimeval): Int `{
284 S_FD_SET *rds, *wts, *exs = NULL;
285 S_TIMEVAL *tm = NULL;
286 if(reads != NULL) rds = (S_FD_SET*)reads;
287 if(write != NULL) wts = (S_FD_SET*)write;
288 if(except != NULL) exs = (S_FD_SET*)except;
289 if(timeout != NULL) tm = (S_TIMEVAL*)timeout;
290 return select(*max, rds, wts, exs, tm);
291 `}
292 end
293
294 extern FFSocketTypes `{ int `}
295 new sock_stream `{ return SOCK_STREAM; `}
296 new sock_dgram `{ return SOCK_DGRAM; `}
297 new sock_raw `{ return SOCK_RAW; `}
298 new sock_seqpacket `{ return SOCK_SEQPACKET; `}
299 end
300 extern FFSocketAddressFamilies `{ int `}
301 new af_null `{ return 0; `}
302 new af_unspec `{ return AF_UNSPEC; `} # unspecified
303 new af_unix `{ return AF_UNIX; `} # local to host (pipes)
304 new af_local `{ return AF_LOCAL; `} # backward compatibility
305 new af_inet `{ return AF_INET; `} # internetwork: UDP, TCP, etc.
306 new af_sna `{ return AF_SNA; `} # IBM SNA
307 new af_decnet `{ return AF_DECnet; `} # DECnet
308 new af_route `{ return AF_ROUTE; `} # Internal Routing Protocol
309 new af_ipx `{ return AF_IPX; `} # Novell Internet Protocol
310 new af_isdn `{ return AF_ISDN; `} # Integrated Services Digital Network
311 new af_inet6 `{ return AF_INET6; `} # IPv6
312 new af_max `{ return AF_MAX; `}
313 end
314 extern FFSocketProtocolFamilies `{ int `}
315 new pf_null `{ return 0; `}
316 new pf_unspec `{ return PF_UNSPEC; `}
317 new pf_local `{ return PF_LOCAL; `}
318 new pf_unix `{ return PF_UNIX; `}
319 new pf_inet `{ return PF_INET; `}
320 new pf_sna `{ return PF_SNA; `}
321 new pf_decnet `{ return PF_DECnet; `}
322 new pf_route `{ return PF_ROUTE; `}
323 new pf_ipx `{ return PF_IPX; `}
324 new pf_isdn `{ return PF_ISDN; `}
325 new pf_key `{ return PF_KEY; `}
326 new pf_inet6 `{ return PF_INET6; `}
327 new pf_max `{ return PF_MAX; `}
328 end
329 # Level on which to set options
330 extern FFSocketOptLevels `{ int `}
331 # Dummy for IP (As defined in C)
332 new ip `{ return IPPROTO_IP;`}
333 # Control message protocol
334 new icmp `{ return IPPROTO_ICMP;`}
335 # Use TCP
336 new tcp `{ return IPPROTO_TCP; `}
337 # Socket level options
338 new socket `{ return SOL_SOCKET; `}
339 end
340 # Options for socket, use with setsockopt
341 extern FFSocketOptNames `{ int `}
342 # Enables debugging information
343 new debug `{ return SO_DEBUG; `}
344 # Authorizes the broadcasting of messages
345 new broadcast `{ return SO_BROADCAST; `}
346 # Authorizes the reuse of the local address
347 new reuseaddr `{ return SO_REUSEADDR; `}
348 # Authorizes the use of keep-alive packets in a connection
349 new keepalive `{ return SO_KEEPALIVE; `}
350 end
351 # Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event
352 extern FFSocketPollValues `{ int `}
353 new pollin `{ return POLLIN; `} # Data other than high-priority data may be read without blocking.
354 new pollrdnorm `{ return POLLRDNORM; `} # Normal data may be read without blocking.
355 new pollrdband `{ return POLLRDBAND; `} # Priority data may be read without blocking.
356 new pollpri `{ return POLLPRI; `} # High-priority data may be read without blocking.
357 new pollout `{ return POLLOUT; `} # Normal data may be written without blocking.
358 new pollwrnorm `{ return POLLWRNORM; `} # Equivalent to POLLOUT
359 new pollwrband `{ return POLLWRBAND; `} # Priority data may be written.
360 new pollerr `{ return POLLERR; `} # An error has occurred on the device or stream. This flag is only valid in the revents bitmask; it shall be ignored in the events member.
361 new pollhup `{ return POLLHUP; `} # The device has been disconnected. This event and POLLOUT are mutually-exclusive; a stream can never be writable if a hangup has occurred. However, this event and POLLIN, POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive. This flag is only valid in the revents bitmask; it shall be ignored in the events member.
362 new pollnval `{ return POLLNVAL; `} # The specified fd value is invalid. This flag is only valid in the revents member; it shall ignored in the events member.
363
364 # Combines two FFSocketPollValues
365 private fun +(other: FFSocketPollValues): FFSocketPollValues `{
366 return recv | other;
367 `}
368 end