1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Low-level socket functionalities
25 #include <sys/socket.h>
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
35 #include <netinet/tcp.h>
38 # Wrapper for the data structure used for polling on a socket
43 private var poll_struct
: NativeSocketPollFD
45 # A collection of the events to be watched
46 var events
: Array[NativeSocketPollValues]
48 # Create a PollFD object from NativePollFD informations
49 init from_poll_values
(pid
: Int, events
: Array[NativeSocketPollValues])
51 assert events
.length
>= 1
53 var events_in_one
= events
[0]
55 for i
in [1 .. events
.length
- 1] do
56 events_in_one
+= events
[i
]
59 var poll_struct
= new NativeSocketPollFD(pid
, events_in_one
)
61 init(poll_struct
, events
)
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]
67 var resp_array
= new Array[NativeSocketPollValues]
69 if c_check_resp
(response
, i
) != 0 then
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
79 return response & mask;
82 redef fun finalize_once
88 # Data structure used by the poll function
89 private extern class NativeSocketPollFD `{ struct pollfd * `}
92 fun fd: Int `{ return self->fd; `}
94 # List of events to be watched
95 fun events
: Int `{ return self->events; `}
97 # List of events received by the last poll function
98 fun revents: Int `{ return self->revents; `}
100 new (pid
: Int, events
: NativeSocketPollValues) `{
101 struct pollfd *poll = malloc(sizeof(struct pollfd));
103 poll->events = events;
109 extern class NativeSocket `{ int* `}
111 # Create a new C socket
112 new socket(domain: NativeSocketAddressFamilies, socketType: NativeSocketTypes, protocol: NativeSocketProtocolFamilies) `{
113 int ds
= socket
(domain
, socketType
, protocol
);
117 int
*d
= malloc
(sizeof
(int
));
118 memcpy
(d
, &ds
, sizeof
(ds
));
123 fun destroy `{ free(self); `}
125 # Close the socket in both read/write
126 fun close
: Int `{ return close(*self); `}
128 # Get the FD related to `self`
129 fun descriptor: Int `{ return *self; `}
131 # Connect to another open socket
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));
138 # Write `length` bytes from `buffer`
139 fun write
(buffer
: CString, length
: Int): Int `{
140 return write(*self, buffer, length);
143 # Write `value` as a single byte
144 fun write_byte
(value
: Byte): Int `{
145 unsigned char byt = (unsigned char)value;
146 return write(*self, &byt, 1);
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);
154 # Sets an option for the socket
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));
165 # Bind the socket to a local address
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)); `}
170 # Prepare for listening to incoming connections
171 fun listen(size: Int): Int `{ return listen(*self, size); `}
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]
176 var result
= native_poll
(filedesc
.poll_struct
, timeout
)
178 return filedesc
.check_response
(result
)
181 # Poll this socket with `POLLHUP|POLLERR`
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);
190 # Call to the poll function of the C socket
193 # int poll(struct pollfd fds[], nfds_t nfds, int timeout);
195 # Official documentation of the poll function:
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);
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;
215 int *ptr = malloc(sizeof(int));
220 # Accept a new connection on `self`
222 # Require the socket to be first bound and listening for connections
223 fun accept
: nullable SocketAcceptResult
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
)
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;
237 flags = flags | O_NONBLOCK;
238 } else if (flags & O_NONBLOCK) {
239 flags = flags - O_NONBLOCK;
243 fcntl(*self, F_SETFL, flags);
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));
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);
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);
263 # Result of a call to `NativeSocket::accept`
264 class SocketAcceptResult
267 var socket
: NativeSocket
269 # Address of the remote client
270 var addr_in
: NativeSocketAddrIn
273 # Socket address in the Internet namespace, pointer to a `struct sockaddr_in`
274 extern class NativeSocketAddrIn `{ struct sockaddr_in* `}
277 new nul `{ return NULL; `}
279 # `malloc` a new instance
281 struct sockaddr_in *sai = NULL;
282 sai = malloc(sizeof(struct sockaddr_in));
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,
294 # Internet address as then IPV4 numbers-and-dots notation
295 fun address
: CString `{ return (char*)inet_ntoa(self->sin_addr); `}
297 # Set `address
` to `INADDR_ANY`
298 fun address_any `{ self->sin_addr.s_addr = INADDR_ANY; `}
300 # Set `address` to `INADDR_BROADCAST`
301 fun address_broadcast
`{ self->sin_addr.s_addr = INADDR_BROADCAST; `}
304 fun family: NativeSocketAddressFamilies `{ return self->sin_family; `}
307 fun family
=(value
: NativeSocketAddressFamilies) `{ self->sin_family = value; `}
310 fun port: Int `{ return ntohs(self->sin_port); `}
313 fun port
=(value
: Int) `{ self->sin_port = htons(value); `}
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];
322 # Alternative names for the host
323 fun h_aliases
: Array[String]
325 var res
= new Array[String]
327 var ha
= native_h_aliases
(res
.length
)
328 if ha
.address_is_null
then break
335 fun h_addr
: CString `{
336 return (char*)inet_ntoa(*(struct in_addr*)self->h_addr);
340 fun h_addrtype
: Int `{ return self->h_addrtype; `}
342 # Length in bytes of the addresses
343 fun h_length: Int `{ return self->h_length; `}
346 fun h_name
: CString `{ return self->h_name; `}
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;
359 # Number of seconds recorded
360 fun seconds
: Int `{ return self->tv_sec; `}
362 # Number of microseconds recorded
363 fun microseconds: Int `{ return self->tv_usec; `}
366 fun destroy
`{ free(self); `}
369 # Structure used to register FDs for a Select
371 # FIXME: This should not be Socket-specific
372 # FIXME: This is Unix-specific
373 extern class NativeSocketSet `{ fd_set* `}
376 f = malloc(sizeof(fd_set));
380 # Add a file descriptor to the set
381 fun set
(s
: NativeSocket) `{ FD_SET(*s, self); `}
383 # Check if `s
` is in the set
384 fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, self); `}
387 fun zero
`{ FD_ZERO(self); `}
389 # Remove `s
` from the set
390 fun clear(s: NativeSocket) `{ FD_CLR(*s, self); `}
393 fun destroy
`{ free(self); `}
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 `{
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
);
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; `}
427 extern class NativeSocketAddressFamilies `{ int `}
429 new af_unspec `{ return AF_UNSPEC; `}
431 # Local to host (pipes)
432 new af_unix
`{ return AF_UNIX; `}
434 # For backward compatibility
435 new af_local `{ return AF_LOCAL; `}
437 # Internetwork: UDP, TCP, etc.
438 new af_inet
`{ return AF_INET; `}
441 new af_sna `{ return AF_SNA; `}
444 new af_decnet
`{ return AF_DECnet; `}
446 # Internal Routing Protocol
447 new af_route `{ return AF_ROUTE; `}
449 # Novell Internet Protocol
450 new af_ipx
`{ return AF_IPX; `}
453 new af_inet6 `{ return AF_INET6; `}
455 # Maximum identifier for socket families
456 new af_max
`{ return AF_MAX; `}
459 # Socket protocol families
460 extern class NativeSocketProtocolFamilies `{ int `}
462 new pf_unspec
`{ return PF_UNSPEC; `}
465 new pf_local `{ return PF_LOCAL; `}
468 new pf_unix
`{ return PF_UNIX; `}
470 # Internet (IPv4) socket
471 new pf_inet `{ return PF_INET; `}
474 new pf_sna
`{ return PF_SNA; `}
477 new pf_decnet `{ return PF_DECnet; `}
479 # Routing tables control
480 new pf_route
`{ return PF_ROUTE; `}
482 # Novell internet protocol
483 new pf_ipx `{ return PF_IPX; `}
485 # Key management protocol
486 new pf_key
`{ return PF_KEY; `}
488 # Internet (IPv6) socket
489 new pf_inet6 `{ return PF_INET6; `}
491 # Maximum identifier for socket families
492 new pf_max
`{ return PF_MAX; `}
495 # Level on which to set options
496 extern class NativeSocketOptLevels `{ int `}
498 # Dummy for IP (As defined in C)
499 new ip
`{ return IPPROTO_IP;`}
501 # Control message protocol
502 new icmp `{ return IPPROTO_ICMP;`}
505 new tcp
`{ return IPPROTO_TCP; `}
507 # Socket level options
508 new socket `{ return SOL_SOCKET; `}
511 # Options for socket, use with setsockopt
512 extern class NativeSocketOptNames `{ int `}
514 # Enables debugging information
515 new debug `{ return SO_DEBUG; `}
517 # Authorizes the broadcasting of messages
518 new broadcast
`{ return SO_BROADCAST; `}
520 # Authorizes the reuse of the local address
521 new reuseaddr `{ return SO_REUSEADDR; `}
523 # Authorizes the use of keep-alive packets in a connection
524 new keepalive
`{ return SO_KEEPALIVE; `}
526 # Disable the Nagle algorithm and send data as soon as possible, in smaller packets
527 new tcp_nodelay `{ return TCP_NODELAY; `}
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 `}
533 # Data other than high-priority data may be read without blocking.
534 new pollin `{ return POLLIN; `}
536 # Normal data may be read without blocking.
537 new pollrdnorm
`{ return POLLRDNORM; `}
539 # Priority data may be read without blocking.
540 new pollrdband `{ return POLLRDBAND; `}
542 # High-priority data may be read without blocking.
543 new pollpri
`{ return POLLPRI; `}
545 # Normal data may be written without blocking.
546 new pollout `{ return POLLOUT; `}
548 # Equivalent to POLLOUT
549 new pollwrnorm
`{ return POLLWRNORM; `}
551 # Priority data may be written.
552 new pollwrband `{ return POLLWRBAND; `}
554 # An error has occurred on the device or stream.
556 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
557 new pollerr
`{ return POLLERR; `}
559 # The device has been disconnected.
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.
565 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
566 new pollhup `{ return POLLHUP; `}
568 # The specified fd value is invalid.
570 # This flag is only valid in the revents member; it shall ignored in the events member.
571 new pollnval
`{ return POLLNVAL; `}
573 # Combines two NativeSocketPollValues
574 private fun +(other: NativeSocketPollValues): NativeSocketPollValues `{
580 # Get network host entry
581 fun gethostbyname(name: CString): NativeSocketHostent `{
582 return gethostbyname
(name
);
585 # Last error raised by `gethostbyname
`
586 fun h_errno: HErrno `{ return h_errno; `}
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; `}
594 # The requested name is valid but does not have an IP address
597 fun no_address
: Bool `{ return self == NO_ADDRESS; `}
599 # The requested name is valid byt does not have an IP address
601 # Same as `no_address
`.
602 fun no_data: Bool `{ return self == NO_DATA; `}
604 # A nonrecoverable name server error occurred
605 fun no_recovery
: Bool `{ return self == NO_RECOVERY; `}
607 # A temporary error occurred on an authoritative name server, try again later
608 fun try_again: Bool `{ return self == TRY_AGAIN; `}
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"
621 # This may happen if another call was made to `gethostbyname`
622 # before we fetch the error code.
623 return "Unknown error on `gethostbyname`"