9930dc625110367c1a20aaccf9350cfa39068de4
[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
32 typedef struct sockaccept_result {
33 struct sockaddr_in addr_in;
34 int s_desc;
35 } S_ACCEPT_RESULT;
36 `}
37
38 # Wrapper for the data structure PollFD used for polling on a socket
39 class PollFD
40
41 # The PollFD object
42 private var poll_struct: FFSocketPollFD
43
44 # A collection of the events to be watched
45 var events: Array[FFSocketPollValues]
46
47 init(pid: Int, events: Array[FFSocketPollValues])
48 do
49 assert events.length >= 1
50 self.events = events
51
52 var events_in_one = events[0]
53
54 for i in [1 .. events.length-1] do
55 events_in_one += events[i]
56 end
57
58 self.poll_struct = new FFSocketPollFD(pid, events_in_one)
59 end
60
61 # Reads the response and returns an array with the type of events that have been found
62 private fun check_response(response: Int): Array[FFSocketPollValues]
63 do
64 var resp_array = new Array[FFSocketPollValues]
65 for i in events do
66 if c_check_resp(response, i) != 0 then
67 resp_array.push(i)
68 end
69 end
70 return resp_array
71 end
72
73 # Checks if the poll call has returned true for a particular type of event
74 private fun c_check_resp(response: Int, mask: FFSocketPollValues): Int
75 `{
76 return response & mask;
77 `}
78
79 end
80
81 # Data structure used by the poll function
82 private extern class FFSocketPollFD `{ struct pollfd `}
83
84 # File descriptor id
85 private fun fd: Int `{ return recv.fd; `}
86
87 # List of events to be watched
88 private fun events: Int `{ return recv.events; `}
89
90 # List of events received by the last poll function
91 private fun revents: Int `{ return recv.revents; `}
92
93 new (pid: Int, events: FFSocketPollValues) `{
94 struct pollfd poll;
95 poll.fd = pid;
96 poll.events = events;
97 return poll;
98 `}
99
100 end
101
102 extern class FFSocket `{ int* `}
103
104 new socket(domain: FFSocketAddressFamilies, socketType: FFSocketTypes, protocol: FFSocketProtocolFamilies) `{
105 int *d = malloc(sizeof(int));
106 int ds = socket(domain, socketType, protocol);
107 if(ds == -1){
108 free(d);
109 return NULL;
110 }
111 memcpy(d, &ds, sizeof(ds));
112 return d;
113 `}
114
115 fun destroy `{ free(recv); `}
116
117 fun close: Int `{ return close(*recv); `}
118
119 fun descriptor: Int `{ return *recv; `}
120
121 fun gethostbyname(n: String): FFSocketHostent import String.to_cstring `{ return gethostbyname(String_to_cstring(n)); `}
122
123 fun connect(addrIn: FFSocketAddrIn): Int `{
124 return connect(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn));
125 `}
126
127 fun write(buffer: String): Int
128 import String.to_cstring, String.length `{
129 return write(*recv, (char*)String_to_cstring(buffer), String_length(buffer));
130 `}
131
132 fun read: String import NativeString.to_s_with_length `{
133 static char c[1024];
134 int n = read(*recv, c, 1024);
135 if(n < 0) {
136 return NativeString_to_s_with_length("",0);
137 }
138 char* ret = malloc(n + 1);
139 memcpy(ret, c, n);
140 ret[n] = '\0';
141 return NativeString_to_s_with_length(ret, n);
142 `}
143
144 # Sets an option for the socket
145 fun setsockopt(level: FFSocketOptLevels, option_name: FFSocketOptNames, option_value: Int) `{
146 int err = setsockopt(*recv, level, option_name, &option_value, sizeof(int));
147 if(err != 0){
148 perror("Error on setsockopts: ");
149 exit(1);
150 }
151 `}
152
153 fun bind(addrIn: FFSocketAddrIn): Int `{ return bind(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
154
155 fun listen(size: Int): Int `{ return listen(*recv, size); `}
156
157 # Checks if the buffer is ready for any event specified when creating the pollfd structure
158 fun socket_poll(filedesc: PollFD, timeout: Int): Array[FFSocketPollValues]
159 do
160 var result = i_poll(filedesc.poll_struct, timeout)
161 assert result != -1
162 return filedesc.check_response(result)
163 end
164
165 # Call to the poll function of the C socket
166 #
167 # Signature:
168 # int poll(struct pollfd fds[], nfds_t nfds, int timeout);
169 #
170 # Official documentation of the poll function:
171 #
172 # The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.
173 # For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events.
174 # The number of pollfd structures in the fds array is specified by nfds.
175 # The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred.
176 # The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor.
177 # It is a pointer to an array with one member for each open file descriptor of interest.
178 # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
179 # OR'ing a combination of the pollfd flags.
180 private fun i_poll(filedesc: FFSocketPollFD, timeout: Int): Int `{
181 int poll_return = poll(&filedesc, 1, timeout);
182 return poll_return;
183 `}
184
185 private fun i_accept(addrIn: FFSocketAddrIn): FFSocket `{
186 socklen_t s = sizeof(struct sockaddr);
187 int *d = NULL;
188 d = malloc(sizeof(int));
189 *d = accept(*recv,(struct sockaddr*)addrIn, &s);
190 return d;
191 `}
192
193 fun accept: FFSocketAcceptResult
194 do
195 var addrIn = new FFSocketAddrIn
196 var s = i_accept(addrIn)
197 return new FFSocketAcceptResult(s, addrIn)
198 end
199 end
200
201 extern class FFSocketAcceptResult `{ S_ACCEPT_RESULT* `}
202 new (socket: FFSocket, addrIn: FFSocketAddrIn) `{
203 S_ACCEPT_RESULT *sar = NULL;
204 sar = malloc(sizeof(S_ACCEPT_RESULT));
205 sar->s_desc = *socket;
206 sar->addr_in = *addrIn;
207 return sar;
208 `}
209
210 fun socket: FFSocket `{ return &recv->s_desc; `}
211
212 fun addrIn: FFSocketAddrIn `{ return &recv->addr_in; `}
213
214 fun destroy `{ free(recv); `}
215 end
216
217 extern class FFSocketAddrIn `{ struct sockaddr_in* `}
218 new `{
219 struct sockaddr_in *sai = NULL;
220 sai = malloc(sizeof(struct sockaddr_in));
221 return sai;
222 `}
223
224 new with(port: Int, family: FFSocketAddressFamilies) `{
225 struct sockaddr_in *sai = NULL;
226 sai = malloc(sizeof(struct sockaddr_in));
227 sai->sin_family = family;
228 sai->sin_port = htons(port);
229 sai->sin_addr.s_addr = INADDR_ANY;
230 return sai;
231 `}
232
233 new with_hostent(hostent: FFSocketHostent, port: Int) `{
234 struct sockaddr_in *sai = NULL;
235 sai = malloc(sizeof(struct sockaddr_in));
236 sai->sin_family = hostent->h_addrtype;
237 sai->sin_port = htons(port);
238 memcpy((char*)&sai->sin_addr.s_addr, (char*)hostent->h_addr, hostent->h_length);
239 return sai;
240 `}
241
242 fun address: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(recv->sin_addr)); `}
243
244 fun family: FFSocketAddressFamilies `{ return recv->sin_family; `}
245
246 fun port: Int `{ return ntohs(recv->sin_port); `}
247
248 fun destroy `{ free(recv); `}
249 end
250
251 extern class FFSocketHostent `{ struct hostent* `}
252 private fun i_h_aliases(i: Int): String import NativeString.to_s `{ return NativeString_to_s(recv->h_aliases[i]); `}
253
254 private fun i_h_aliases_reachable(i: Int): Bool `{ return (recv->h_aliases[i] != NULL); `}
255
256 fun h_aliases: Array[String]
257 do
258 var i=0
259 var d=new Array[String]
260 loop
261 d.add(i_h_aliases(i))
262 if i_h_aliases_reachable(i+1) == false then break
263 i += 1
264 end
265 return d
266 end
267
268 fun h_addr: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(*(struct in_addr*)recv->h_addr)); `}
269
270 fun h_addrtype: Int `{ return recv->h_addrtype; `}
271
272 fun h_length: Int `{ return recv->h_length; `}
273
274 fun h_name: String import NativeString.to_s `{ return NativeString_to_s(recv->h_name); `}
275 end
276
277 extern class FFTimeval `{ struct timeval* `}
278 new (seconds: Int, microseconds: Int) `{
279 struct timeval* tv = NULL;
280 tv = malloc(sizeof(struct timeval));
281 tv->tv_sec = seconds;
282 tv->tv_usec = microseconds;
283 return tv;
284 `}
285
286 fun seconds: Int `{ return recv->tv_sec; `}
287
288 fun microseconds: Int `{ return recv->tv_usec; `}
289
290 fun destroy `{ free(recv); `}
291 end
292
293 extern class FFSocketSet `{ fd_set* `}
294 new `{
295 fd_set *f = NULL;
296 f = malloc(sizeof(fd_set));
297 return f;
298 `}
299
300 fun set(s: FFSocket) `{ FD_SET(*s, recv); `}
301
302 fun is_set(s: FFSocket): Bool `{ return FD_ISSET(*s, recv); `}
303
304 fun zero `{ FD_ZERO(recv); `}
305
306 fun clear(s: FFSocket) `{ FD_CLR(*s, recv); `}
307
308 fun destroy `{ free(recv); `}
309 end
310
311 class FFSocketObserver
312 fun select(max: FFSocket, reads: nullable FFSocketSet, write: nullable FFSocketSet,
313 except: nullable FFSocketSet, timeout: FFTimeval): Int `{
314 fd_set *rds, *wts, *exs = NULL;
315 struct timeval *tm = NULL;
316 if (reads != NULL) rds = (fd_set*)reads;
317 if (write != NULL) wts = (fd_set*)write;
318 if (except != NULL) exs = (fd_set*)except;
319 if (timeout != NULL) tm = (struct timeval*)timeout;
320 return select(*max, rds, wts, exs, tm);
321 `}
322 end
323
324 extern class FFSocketTypes `{ int `}
325 new sock_stream `{ return SOCK_STREAM; `}
326 new sock_dgram `{ return SOCK_DGRAM; `}
327 new sock_raw `{ return SOCK_RAW; `}
328 new sock_seqpacket `{ return SOCK_SEQPACKET; `}
329 end
330
331 extern class FFSocketAddressFamilies `{ int `}
332 new af_null `{ return 0; `}
333
334 # Unspecified
335 new af_unspec `{ return AF_UNSPEC; `}
336
337 # Local to host (pipes)
338 new af_unix `{ return AF_UNIX; `}
339
340 # For backward compatibility
341 new af_local `{ return AF_LOCAL; `}
342
343 # Internetwork: UDP, TCP, etc.
344 new af_inet `{ return AF_INET; `}
345
346 # IBM SNA
347 new af_sna `{ return AF_SNA; `}
348
349 # DECnet
350 new af_decnet `{ return AF_DECnet; `}
351
352 # Internal Routing Protocol
353 new af_route `{ return AF_ROUTE; `}
354
355 # Novell Internet Protocol
356 new af_ipx `{ return AF_IPX; `}
357
358 # Integrated Services Digital Network
359 new af_isdn `{ return AF_ISDN; `}
360
361 # IPv6
362 new af_inet6 `{ return AF_INET6; `}
363
364 new af_max `{ return AF_MAX; `}
365 end
366
367 extern class FFSocketProtocolFamilies `{ int `}
368 new pf_null `{ return 0; `}
369 new pf_unspec `{ return PF_UNSPEC; `}
370 new pf_local `{ return PF_LOCAL; `}
371 new pf_unix `{ return PF_UNIX; `}
372 new pf_inet `{ return PF_INET; `}
373 new pf_sna `{ return PF_SNA; `}
374 new pf_decnet `{ return PF_DECnet; `}
375 new pf_route `{ return PF_ROUTE; `}
376 new pf_ipx `{ return PF_IPX; `}
377 new pf_isdn `{ return PF_ISDN; `}
378 new pf_key `{ return PF_KEY; `}
379 new pf_inet6 `{ return PF_INET6; `}
380 new pf_max `{ return PF_MAX; `}
381 end
382
383 # Level on which to set options
384 extern class FFSocketOptLevels `{ int `}
385
386 # Dummy for IP (As defined in C)
387 new ip `{ return IPPROTO_IP;`}
388
389 # Control message protocol
390 new icmp `{ return IPPROTO_ICMP;`}
391
392 # Use TCP
393 new tcp `{ return IPPROTO_TCP; `}
394
395 # Socket level options
396 new socket `{ return SOL_SOCKET; `}
397 end
398
399 # Options for socket, use with setsockopt
400 extern class FFSocketOptNames `{ int `}
401
402 # Enables debugging information
403 new debug `{ return SO_DEBUG; `}
404
405 # Authorizes the broadcasting of messages
406 new broadcast `{ return SO_BROADCAST; `}
407
408 # Authorizes the reuse of the local address
409 new reuseaddr `{ return SO_REUSEADDR; `}
410
411 # Authorizes the use of keep-alive packets in a connection
412 new keepalive `{ return SO_KEEPALIVE; `}
413 end
414
415 # Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event
416 extern class FFSocketPollValues `{ int `}
417
418 # Data other than high-priority data may be read without blocking.
419 new pollin `{ return POLLIN; `}
420
421 # Normal data may be read without blocking.
422 new pollrdnorm `{ return POLLRDNORM; `}
423
424 # Priority data may be read without blocking.
425 new pollrdband `{ return POLLRDBAND; `}
426
427 # High-priority data may be read without blocking.
428 new pollpri `{ return POLLPRI; `}
429
430 # Normal data may be written without blocking.
431 new pollout `{ return POLLOUT; `}
432
433 # Equivalent to POLLOUT
434 new pollwrnorm `{ return POLLWRNORM; `}
435
436 # Priority data may be written.
437 new pollwrband `{ return POLLWRBAND; `}
438
439 # An error has occurred on the device or stream.
440 #
441 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
442 new pollerr `{ return POLLERR; `}
443
444 # The device has been disconnected.
445 #
446 # This event and POLLOUT are mutually-exclusive; a stream can never be
447 # writable if a hangup has occurred. However, this event and POLLIN,
448 # POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive.
449 #
450 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
451 new pollhup `{ return POLLHUP; `}
452
453 # The specified fd value is invalid.
454 #
455 # This flag is only valid in the revents member; it shall ignored in the events member.
456 new pollnval `{ return POLLNVAL; `}
457
458 # Combines two FFSocketPollValues
459 private fun +(other: FFSocketPollValues): FFSocketPollValues `{
460 return recv | other;
461 `}
462 end