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