lib/socket: add UDP related services to the native layer
[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
33 in "C" `{
34 #include <fcntl.h>
35 #include <netinet/tcp.h>
36 `}
37
38 # Wrapper for the data structure PollFD used for polling on a socket
39 class PollFD
40 super FinalizableOnce
41
42 # The PollFD object
43 private var poll_struct: NativeSocketPollFD
44
45 # A collection of the events to be watched
46 var events: Array[NativeSocketPollValues]
47
48 init(pid: Int, events: Array[NativeSocketPollValues])
49 do
50 assert events.length >= 1
51 self.events = events
52
53 var events_in_one = events[0]
54
55 for i in [1 .. events.length-1] do
56 events_in_one += events[i]
57 end
58
59 self.poll_struct = new NativeSocketPollFD(pid, events_in_one)
60 end
61
62 # Reads the response and returns an array with the type of events that have been found
63 private fun check_response(response: Int): Array[NativeSocketPollValues]
64 do
65 var resp_array = new Array[NativeSocketPollValues]
66 for i in events do
67 if c_check_resp(response, i) != 0 then
68 resp_array.push(i)
69 end
70 end
71 return resp_array
72 end
73
74 # Checks if the poll call has returned true for a particular type of event
75 private fun c_check_resp(response: Int, mask: NativeSocketPollValues): Int
76 `{
77 return response & mask;
78 `}
79
80 redef fun finalize_once
81 do
82 poll_struct.free
83 end
84 end
85
86 # Data structure used by the poll function
87 private extern class NativeSocketPollFD `{ struct pollfd * `}
88
89 # File descriptor
90 fun fd: Int `{ return self->fd; `}
91
92 # List of events to be watched
93 fun events: Int `{ return self->events; `}
94
95 # List of events received by the last poll function
96 fun revents: Int `{ return self->revents; `}
97
98 new (pid: Int, events: NativeSocketPollValues) `{
99 struct pollfd *poll = malloc(sizeof(struct pollfd));
100 poll->fd = pid;
101 poll->events = events;
102 return poll;
103 `}
104 end
105
106 extern class NativeSocket `{ int* `}
107
108 new socket(domain: NativeSocketAddressFamilies, socketType: NativeSocketTypes, protocol: NativeSocketProtocolFamilies) `{
109 int ds = socket(domain, socketType, protocol);
110 if(ds == -1){
111 return NULL;
112 }
113 int *d = malloc(sizeof(int));
114 memcpy(d, &ds, sizeof(ds));
115 return d;
116 `}
117
118 fun destroy `{ free(self); `}
119
120 fun close: Int `{ return close(*self); `}
121
122 fun descriptor: Int `{ return *self; `}
123
124 fun connect(addrIn: NativeSocketAddrIn): Int `{
125 return connect(*self, (struct sockaddr*)addrIn, sizeof(*addrIn));
126 `}
127
128 # Write `length` bytes from `buffer`
129 fun write(buffer: NativeString, length: Int): Int `{
130 return write(*self, buffer, length);
131 `}
132
133 # Write `value` as a single byte
134 fun write_byte(value: Byte): Int `{
135 unsigned char byt = (unsigned char)value;
136 return write(*self, &byt, 1);
137 `}
138
139 # Read `length` bytes into `buffer`, returns the number of bytes read
140 fun read(buffer: NativeString, length: Int): Int `{
141 return read(*self, buffer, length);
142 `}
143
144 # Sets an option for the socket
145 #
146 # Returns `true` on success.
147 fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int): Bool `{
148 int err = setsockopt(*self, level, option_name, &option_value, sizeof(int));
149 if(err != 0){
150 return 0;
151 }
152 return 1;
153 `}
154
155 fun bind(addrIn: NativeSocketAddrIn): Int `{ return bind(*self, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
156
157 fun listen(size: Int): Int `{ return listen(*self, size); `}
158
159 # Checks if the buffer is ready for any event specified when creating the pollfd structure
160 fun socket_poll(filedesc: PollFD, timeout: Int): Array[NativeSocketPollValues]
161 do
162 var result = native_poll(filedesc.poll_struct, timeout)
163 assert result != -1
164 return filedesc.check_response(result)
165 end
166
167 # Call to the poll function of the C socket
168 #
169 # Signature:
170 # int poll(struct pollfd fds[], nfds_t nfds, int timeout);
171 #
172 # Official documentation of the poll function:
173 #
174 # The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.
175 # For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events.
176 # The number of pollfd structures in the fds array is specified by nfds.
177 # The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred.
178 # The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor.
179 # It is a pointer to an array with one member for each open file descriptor of interest.
180 # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
181 # OR'ing a combination of the pollfd flags.
182 private fun native_poll(filedesc: NativeSocketPollFD, timeout: Int): Int `{
183 int poll_return = poll(filedesc, 1, timeout);
184 return poll_return;
185 `}
186
187 private fun native_accept(addr_in: NativeSocketAddrIn): NativeSocket `{
188 socklen_t s = sizeof(struct sockaddr);
189 int socket = accept(*self, (struct sockaddr*)addr_in, &s);
190 if (socket == -1) return NULL;
191
192 int *ptr = malloc(sizeof(int));
193 *ptr = socket;
194 return ptr;
195 `}
196
197 fun accept: nullable SocketAcceptResult
198 do
199 var addrIn = new NativeSocketAddrIn
200 var s = native_accept(addrIn)
201 if s.address_is_null then return null
202 return new SocketAcceptResult(s, addrIn)
203 end
204
205 # Set whether this socket is non blocking
206 fun non_blocking=(value: Bool) `{
207 int flags = fcntl(*self, F_GETFL, 0);
208 if (flags == -1) flags = 0;
209
210 if (value) {
211 flags = flags | O_NONBLOCK;
212 } else if (flags & O_NONBLOCK) {
213 flags = flags - O_NONBLOCK;
214 } else {
215 return;
216 }
217 fcntl(*self, F_SETFL, flags);
218 `}
219
220 # Send `len` bytes from `buf` to `dest_addr`
221 fun sendto(buf: NativeString, len: Int, flags: Int, dest_addr: NativeSocketAddrIn): Int `{
222 return sendto(*self, buf, len, flags, (struct sockaddr*)dest_addr, sizeof(struct sockaddr_in));
223 `}
224
225 # Receive a message into `buf` of maximum `len` bytes
226 fun recv(buf: NativeString, len: Int, flags: Int): Int `{
227 return recv(*self, buf, len, flags);
228 `}
229
230 # Receive a message into `buf` of maximum `len` bytes and store sender info into `src_addr`
231 fun recvfrom(buf: NativeString, len: Int, flags: Int, src_addr: NativeSocketAddrIn): Int `{
232 socklen_t srclen = sizeof(struct sockaddr_in);
233 return recvfrom(*self, buf, len, flags, (struct sockaddr*)src_addr, &srclen);
234 `}
235 end
236
237 # Result of a call to `NativeSocket::accept`
238 class SocketAcceptResult
239
240 # Opened socket
241 var socket: NativeSocket
242
243 # Address of the remote client
244 var addr_in: NativeSocketAddrIn
245 end
246
247 # Socket address in the Internet namespace, pointer to a `struct sockaddr_in`
248 extern class NativeSocketAddrIn `{ struct sockaddr_in* `}
249
250 # `NULL` pointer
251 new nul `{ return NULL; `}
252
253 # `malloc` a new instance
254 new `{
255 struct sockaddr_in *sai = NULL;
256 sai = malloc(sizeof(struct sockaddr_in));
257 return sai;
258 `}
259
260 # Set `address` and `family` from `hostent` (to use with `Sys::gethostbyname`)
261 fun fill_from_hostent(hostent: NativeSocketHostent) `{
262 self->sin_family = hostent->h_addrtype;
263 memcpy((char*)&self->sin_addr.s_addr,
264 (char*)hostent->h_addr,
265 hostent->h_length);
266 `}
267
268 # Internet address as then IPV4 numbers-and-dots notation
269 fun address: NativeString `{ return (char*)inet_ntoa(self->sin_addr); `}
270
271 # Set `address` to `INADDR_ANY`
272 fun address_any `{ self->sin_addr.s_addr = INADDR_ANY; `}
273
274 # Set `address` to `INADDR_BROADCAST`
275 fun address_broadcast `{ self->sin_addr.s_addr = INADDR_BROADCAST; `}
276
277 # Address family
278 fun family: NativeSocketAddressFamilies `{ return self->sin_family; `}
279
280 # Address family
281 fun family=(value: NativeSocketAddressFamilies) `{ self->sin_family = value; `}
282
283 # Port
284 fun port: Int `{ return ntohs(self->sin_port); `}
285
286 # Port
287 fun port=(value: Int) `{ self->sin_port = htons(value); `}
288 end
289
290 # Host entry information, a pointer to a `struct hostent`
291 extern class NativeSocketHostent `{ struct hostent* `}
292 private fun native_h_aliases(i: Int): NativeString `{
293 return self->h_aliases[i];
294 `}
295
296 # Alternative names for the host
297 fun h_aliases: Array[String]
298 do
299 var res = new Array[String]
300 loop
301 var ha = native_h_aliases(res.length)
302 if ha.address_is_null then break
303 res.add ha.to_s
304 end
305 return res
306 end
307
308 fun h_addr: NativeString `{
309 return (char*)inet_ntoa(*(struct in_addr*)self->h_addr);
310 `}
311
312 fun h_addrtype: Int `{ return self->h_addrtype; `}
313
314 fun h_length: Int `{ return self->h_length; `}
315
316 fun h_name: NativeString `{ return self->h_name; `}
317 end
318
319 extern class NativeTimeval `{ struct timeval* `}
320 new (seconds: Int, microseconds: Int) `{
321 struct timeval* tv = NULL;
322 tv = malloc(sizeof(struct timeval));
323 tv->tv_sec = seconds;
324 tv->tv_usec = microseconds;
325 return tv;
326 `}
327
328 fun seconds: Int `{ return self->tv_sec; `}
329
330 fun microseconds: Int `{ return self->tv_usec; `}
331
332 fun destroy `{ free(self); `}
333 end
334
335 extern class NativeSocketSet `{ fd_set* `}
336 new `{
337 fd_set *f = NULL;
338 f = malloc(sizeof(fd_set));
339 return f;
340 `}
341
342 fun set(s: NativeSocket) `{ FD_SET(*s, self); `}
343
344 fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, self); `}
345
346 fun zero `{ FD_ZERO(self); `}
347
348 fun clear(s: NativeSocket) `{ FD_CLR(*s, self); `}
349
350 fun destroy `{ free(self); `}
351 end
352
353 class NativeSocketObserver
354 # FIXME this implementation is broken. `reads`, `write` and `except`
355 # are boxed objects, passing them to a C function is illegal.
356 fun select(max: NativeSocket, reads: nullable NativeSocketSet, write: nullable NativeSocketSet,
357 except: nullable NativeSocketSet, timeout: NativeTimeval): Int `{
358 fd_set *rds = NULL,
359 *wts = NULL,
360 *exs = NULL;
361 struct timeval *tm = NULL;
362 if (reads != NULL) rds = (fd_set*)reads;
363 if (write != NULL) wts = (fd_set*)write;
364 if (except != NULL) exs = (fd_set*)except;
365 if (timeout != NULL) tm = (struct timeval*)timeout;
366 return select(*max, rds, wts, exs, tm);
367 `}
368 end
369
370 extern class NativeSocketTypes `{ int `}
371 new sock_stream `{ return SOCK_STREAM; `}
372 new sock_dgram `{ return SOCK_DGRAM; `}
373 new sock_raw `{ return SOCK_RAW; `}
374 new sock_seqpacket `{ return SOCK_SEQPACKET; `}
375 end
376
377 extern class NativeSocketAddressFamilies `{ int `}
378 new af_null `{ return 0; `}
379
380 # Unspecified
381 new af_unspec `{ return AF_UNSPEC; `}
382
383 # Local to host (pipes)
384 new af_unix `{ return AF_UNIX; `}
385
386 # For backward compatibility
387 new af_local `{ return AF_LOCAL; `}
388
389 # Internetwork: UDP, TCP, etc.
390 new af_inet `{ return AF_INET; `}
391
392 # IBM SNA
393 new af_sna `{ return AF_SNA; `}
394
395 # DECnet
396 new af_decnet `{ return AF_DECnet; `}
397
398 # Internal Routing Protocol
399 new af_route `{ return AF_ROUTE; `}
400
401 # Novell Internet Protocol
402 new af_ipx `{ return AF_IPX; `}
403
404 # IPv6
405 new af_inet6 `{ return AF_INET6; `}
406
407 new af_max `{ return AF_MAX; `}
408 end
409
410 extern class NativeSocketProtocolFamilies `{ int `}
411 new pf_null `{ return 0; `}
412 new pf_unspec `{ return PF_UNSPEC; `}
413 new pf_local `{ return PF_LOCAL; `}
414 new pf_unix `{ return PF_UNIX; `}
415 new pf_inet `{ return PF_INET; `}
416 new pf_sna `{ return PF_SNA; `}
417 new pf_decnet `{ return PF_DECnet; `}
418 new pf_route `{ return PF_ROUTE; `}
419 new pf_ipx `{ return PF_IPX; `}
420 new pf_key `{ return PF_KEY; `}
421 new pf_inet6 `{ return PF_INET6; `}
422 new pf_max `{ return PF_MAX; `}
423 new ipproto_udp `{ return IPPROTO_UDP; `}
424 end
425
426 # Level on which to set options
427 extern class NativeSocketOptLevels `{ int `}
428
429 # Dummy for IP (As defined in C)
430 new ip `{ return IPPROTO_IP;`}
431
432 # Control message protocol
433 new icmp `{ return IPPROTO_ICMP;`}
434
435 # Use TCP
436 new tcp `{ return IPPROTO_TCP; `}
437
438 # Socket level options
439 new socket `{ return SOL_SOCKET; `}
440 end
441
442 # Options for socket, use with setsockopt
443 extern class NativeSocketOptNames `{ int `}
444
445 # Enables debugging information
446 new debug `{ return SO_DEBUG; `}
447
448 # Authorizes the broadcasting of messages
449 new broadcast `{ return SO_BROADCAST; `}
450
451 # Authorizes the reuse of the local address
452 new reuseaddr `{ return SO_REUSEADDR; `}
453
454 # Authorizes the use of keep-alive packets in a connection
455 new keepalive `{ return SO_KEEPALIVE; `}
456
457 # Disable the Nagle algorithm and send data as soon as possible, in smaller packets
458 new tcp_nodelay `{ return TCP_NODELAY; `}
459 end
460
461 # Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event
462 extern class NativeSocketPollValues `{ int `}
463
464 # Data other than high-priority data may be read without blocking.
465 new pollin `{ return POLLIN; `}
466
467 # Normal data may be read without blocking.
468 new pollrdnorm `{ return POLLRDNORM; `}
469
470 # Priority data may be read without blocking.
471 new pollrdband `{ return POLLRDBAND; `}
472
473 # High-priority data may be read without blocking.
474 new pollpri `{ return POLLPRI; `}
475
476 # Normal data may be written without blocking.
477 new pollout `{ return POLLOUT; `}
478
479 # Equivalent to POLLOUT
480 new pollwrnorm `{ return POLLWRNORM; `}
481
482 # Priority data may be written.
483 new pollwrband `{ return POLLWRBAND; `}
484
485 # An error has occurred on the device or stream.
486 #
487 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
488 new pollerr `{ return POLLERR; `}
489
490 # The device has been disconnected.
491 #
492 # This event and POLLOUT are mutually-exclusive; a stream can never be
493 # writable if a hangup has occurred. However, this event and POLLIN,
494 # POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive.
495 #
496 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
497 new pollhup `{ return POLLHUP; `}
498
499 # The specified fd value is invalid.
500 #
501 # This flag is only valid in the revents member; it shall ignored in the events member.
502 new pollnval `{ return POLLNVAL; `}
503
504 # Combines two NativeSocketPollValues
505 private fun +(other: NativeSocketPollValues): NativeSocketPollValues `{
506 return self | other;
507 `}
508 end
509
510 redef class Sys
511 # Get network host entry
512 fun gethostbyname(name: NativeString): NativeSocketHostent `{
513 return gethostbyname(name);
514 `}
515
516 # Last error raised by `gethostbyname`
517 fun h_errno: HErrno `{ return h_errno; `}
518 end
519
520 # Error code of `Sys::h_errno`
521 extern class HErrno `{ int `}
522 # The specified host is unknown
523 fun host_not_found: Bool `{ return self == HOST_NOT_FOUND; `}
524
525 # The requested name is valid but does not have an IP address
526 #
527 # Same as `no_data`.
528 fun no_address: Bool `{ return self == NO_ADDRESS; `}
529
530 # The requested name is valid byt does not have an IP address
531 #
532 # Same as `no_address`.
533 fun no_data: Bool `{ return self == NO_DATA; `}
534
535 # A nonrecoverable name server error occurred
536 fun no_recovery: Bool `{ return self == NO_RECOVERY; `}
537
538 # A temporary error occurred on an authoritative name server, try again later
539 fun try_again: Bool `{ return self == TRY_AGAIN; `}
540
541 redef fun to_s
542 do
543 if host_not_found then
544 return "The specified host is unknown"
545 else if no_address then
546 return "The requested name is valid but does not have an IP address"
547 else if no_recovery then
548 return "A nonrecoverable name server error occurred"
549 else if try_again then
550 return "A temporary error occurred on an authoritative name server, try again later"
551 else
552 # This may happen if another call was made to `gethostbyname`
553 # before we fetch the error code.
554 return "Unknown error on `gethostbyname`"
555 end
556 end
557 end