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 PollFD 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 init(pid
: Int, events
: Array[NativeSocketPollValues])
50 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 self.poll_struct
= new NativeSocketPollFD(pid
, events_in_one
)
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]
65 var resp_array
= new Array[NativeSocketPollValues]
67 if c_check_resp
(response
, i
) != 0 then
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
77 return response & mask;
80 redef fun finalize_once
86 # Data structure used by the poll function
87 private extern class NativeSocketPollFD `{ struct pollfd * `}
90 fun fd: Int `{ return self->fd; `}
92 # List of events to be watched
93 fun events
: Int `{ return self->events; `}
95 # List of events received by the last poll function
96 fun revents: Int `{ return self->revents; `}
98 new (pid
: Int, events
: NativeSocketPollValues) `{
99 struct pollfd *poll = malloc(sizeof(struct pollfd));
101 poll->events = events;
106 extern class NativeSocket `{ int* `}
108 new socket(domain: NativeSocketAddressFamilies, socketType: NativeSocketTypes, protocol: NativeSocketProtocolFamilies) `{
109 int ds
= socket
(domain
, socketType
, protocol
);
113 int
*d
= malloc
(sizeof
(int
));
114 memcpy
(d
, &ds
, sizeof
(ds
));
118 fun destroy `{ free(self); `}
120 fun close
: Int `{ return close(*self); `}
122 fun descriptor: Int `{ return *self; `}
124 fun connect
(addrIn
: NativeSocketAddrIn): Int `{
125 return connect(*self, (struct sockaddr*)addrIn, sizeof(*addrIn));
128 # Write `length` bytes from `buffer`
129 fun write
(buffer
: NativeString, length
: Int): Int `{
130 return write(*self, buffer, length);
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);
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);
144 # Sets an option for the socket
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));
155 fun bind
(addrIn
: NativeSocketAddrIn): Int `{ return bind(*self, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
157 fun listen(size: Int): Int `{ return listen(*self, size); `}
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]
162 var result
= native_poll
(filedesc
.poll_struct
, timeout
)
164 return filedesc
.check_response
(result
)
167 # Poll this socket with `POLLHUP|POLLERR`
169 # A return value of 0 means there is no errors.
170 fun poll_hup_err
: Int `{
171 struct pollfd fd = {*self, POLLHUP|POLLERR, 0};
172 int res = poll(&fd, 1, 0);
176 # Call to the poll function of the C socket
179 # int poll(struct pollfd fds[], nfds_t nfds, int timeout);
181 # Official documentation of the poll function:
183 # The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.
184 # For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events.
185 # The number of pollfd structures in the fds array is specified by nfds.
186 # The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred.
187 # The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor.
188 # It is a pointer to an array with one member for each open file descriptor of interest.
189 # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
190 # OR'ing a combination of the pollfd flags.
191 private fun native_poll
(filedesc
: NativeSocketPollFD, timeout
: Int): Int `{
192 int poll_return = poll(filedesc, 1, timeout);
196 private fun native_accept
(addr_in
: NativeSocketAddrIn): NativeSocket `{
197 socklen_t s = sizeof(struct sockaddr);
198 int socket = accept(*self, (struct sockaddr*)addr_in, &s);
199 if (socket == -1) return NULL;
201 int *ptr = malloc(sizeof(int));
206 fun accept
: nullable SocketAcceptResult
208 var addrIn
= new NativeSocketAddrIn
209 var s
= native_accept
(addrIn
)
210 if s
.address_is_null
then return null
211 return new SocketAcceptResult(s
, addrIn
)
214 # Set whether this socket is non blocking
215 fun non_blocking
=(value
: Bool) `{
216 int flags = fcntl(*self, F_GETFL, 0);
217 if (flags == -1) flags = 0;
220 flags = flags | O_NONBLOCK;
221 } else if (flags & O_NONBLOCK) {
222 flags = flags - O_NONBLOCK;
226 fcntl(*self, F_SETFL, flags);
229 # Send `len` bytes from `buf` to `dest_addr`
230 fun sendto
(buf
: NativeString, len
: Int, flags
: Int, dest_addr
: NativeSocketAddrIn): Int `{
231 return sendto(*self, buf, len, flags, (struct sockaddr*)dest_addr, sizeof(struct sockaddr_in));
234 # Receive a message into `buf` of maximum `len` bytes
235 fun recv
(buf
: NativeString, len
: Int, flags
: Int): Int `{
236 return recv(*self, buf, len, flags);
239 # Receive a message into `buf` of maximum `len` bytes and store sender info into `src_addr`
240 fun recvfrom
(buf
: NativeString, len
: Int, flags
: Int, src_addr
: NativeSocketAddrIn): Int `{
241 socklen_t srclen = sizeof(struct sockaddr_in);
242 return recvfrom(*self, buf, len, flags, (struct sockaddr*)src_addr, &srclen);
246 # Result of a call to `NativeSocket::accept`
247 class SocketAcceptResult
250 var socket
: NativeSocket
252 # Address of the remote client
253 var addr_in
: NativeSocketAddrIn
256 # Socket address in the Internet namespace, pointer to a `struct sockaddr_in`
257 extern class NativeSocketAddrIn `{ struct sockaddr_in* `}
260 new nul `{ return NULL; `}
262 # `malloc` a new instance
264 struct sockaddr_in *sai = NULL;
265 sai = malloc(sizeof(struct sockaddr_in));
269 # Set `address` and `family` from `hostent` (to use with `Sys::gethostbyname`)
270 fun fill_from_hostent
(hostent
: NativeSocketHostent) `{
271 self->sin_family = hostent->h_addrtype;
272 memcpy((char*)&self->sin_addr.s_addr,
273 (char*)hostent->h_addr,
277 # Internet address as then IPV4 numbers-and-dots notation
278 fun address
: NativeString `{ return (char*)inet_ntoa(self->sin_addr); `}
280 # Set `address
` to `INADDR_ANY`
281 fun address_any `{ self->sin_addr.s_addr = INADDR_ANY; `}
283 # Set `address` to `INADDR_BROADCAST`
284 fun address_broadcast
`{ self->sin_addr.s_addr = INADDR_BROADCAST; `}
287 fun family: NativeSocketAddressFamilies `{ return self->sin_family; `}
290 fun family
=(value
: NativeSocketAddressFamilies) `{ self->sin_family = value; `}
293 fun port: Int `{ return ntohs(self->sin_port); `}
296 fun port
=(value
: Int) `{ self->sin_port = htons(value); `}
299 # Host entry information, a pointer to a `struct hostent
`
300 extern class NativeSocketHostent `{ struct hostent* `}
301 private fun native_h_aliases
(i
: Int): NativeString `{
302 return self->h_aliases[i];
305 # Alternative names for the host
306 fun h_aliases
: Array[String]
308 var res
= new Array[String]
310 var ha
= native_h_aliases
(res
.length
)
311 if ha
.address_is_null
then break
317 fun h_addr
: NativeString `{
318 return (char*)inet_ntoa(*(struct in_addr*)self->h_addr);
321 fun h_addrtype
: Int `{ return self->h_addrtype; `}
323 fun h_length: Int `{ return self->h_length; `}
325 fun h_name
: NativeString `{ return self->h_name; `}
328 extern class NativeTimeval `{ struct timeval* `}
329 new (seconds
: Int, microseconds
: Int) `{
330 struct timeval* tv = NULL;
331 tv = malloc(sizeof(struct timeval));
332 tv->tv_sec = seconds;
333 tv->tv_usec = microseconds;
337 fun seconds
: Int `{ return self->tv_sec; `}
339 fun microseconds: Int `{ return self->tv_usec; `}
341 fun destroy
`{ free(self); `}
344 extern class NativeSocketSet `{ fd_set* `}
347 f = malloc(sizeof(fd_set));
351 fun set
(s
: NativeSocket) `{ FD_SET(*s, self); `}
353 fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, self); `}
355 fun zero
`{ FD_ZERO(self); `}
357 fun clear(s: NativeSocket) `{ FD_CLR(*s, self); `}
359 fun destroy
`{ free(self); `}
362 class NativeSocketObserver
363 # FIXME this implementation is broken. `reads
`, `write
` and `except
`
364 # are boxed objects, passing them to a C function is illegal.
365 fun select(max: NativeSocket, reads: nullable NativeSocketSet, write: nullable NativeSocketSet,
366 except: nullable NativeSocketSet, timeout: NativeTimeval): Int `{
370 struct timeval
*tm
= NULL;
371 if (reads
!= NULL) rds
= (fd_set
*)reads
;
372 if (write
!= NULL) wts
= (fd_set
*)write
;
373 if (except
!= NULL) exs
= (fd_set
*)except
;
374 if (timeout
!= NULL) tm
= (struct timeval
*)timeout
;
375 return select
(*max
, rds
, wts
, exs
, tm
);
379 extern class NativeSocketTypes `{ int `}
380 new sock_stream
`{ return SOCK_STREAM; `}
381 new sock_dgram `{ return SOCK_DGRAM; `}
382 new sock_raw
`{ return SOCK_RAW; `}
383 new sock_seqpacket `{ return SOCK_SEQPACKET; `}
386 extern class NativeSocketAddressFamilies `{ int `}
387 new af_null `{ return 0; `}
390 new af_unspec
`{ return AF_UNSPEC; `}
392 # Local to host (pipes)
393 new af_unix `{ return AF_UNIX; `}
395 # For backward compatibility
396 new af_local
`{ return AF_LOCAL; `}
398 # Internetwork: UDP, TCP, etc.
399 new af_inet `{ return AF_INET; `}
402 new af_sna
`{ return AF_SNA; `}
405 new af_decnet `{ return AF_DECnet; `}
407 # Internal Routing Protocol
408 new af_route
`{ return AF_ROUTE; `}
410 # Novell Internet Protocol
411 new af_ipx `{ return AF_IPX; `}
414 new af_inet6
`{ return AF_INET6; `}
416 new af_max `{ return AF_MAX; `}
419 extern class NativeSocketProtocolFamilies `{ int `}
420 new pf_null `{ return 0; `}
421 new pf_unspec
`{ return PF_UNSPEC; `}
422 new pf_local `{ return PF_LOCAL; `}
423 new pf_unix
`{ return PF_UNIX; `}
424 new pf_inet `{ return PF_INET; `}
425 new pf_sna
`{ return PF_SNA; `}
426 new pf_decnet `{ return PF_DECnet; `}
427 new pf_route
`{ return PF_ROUTE; `}
428 new pf_ipx `{ return PF_IPX; `}
429 new pf_key
`{ return PF_KEY; `}
430 new pf_inet6 `{ return PF_INET6; `}
431 new pf_max
`{ return PF_MAX; `}
432 new ipproto_udp `{ return IPPROTO_UDP; `}
435 # Level on which to set options
436 extern class NativeSocketOptLevels `{ int `}
438 # Dummy for IP (As defined in C)
439 new ip `{ return IPPROTO_IP;`}
441 # Control message protocol
442 new icmp
`{ return IPPROTO_ICMP;`}
445 new tcp `{ return IPPROTO_TCP; `}
447 # Socket level options
448 new socket
`{ return SOL_SOCKET; `}
451 # Options for socket, use with setsockopt
452 extern class NativeSocketOptNames `{ int `}
454 # Enables debugging information
455 new debug
`{ return SO_DEBUG; `}
457 # Authorizes the broadcasting of messages
458 new broadcast `{ return SO_BROADCAST; `}
460 # Authorizes the reuse of the local address
461 new reuseaddr
`{ return SO_REUSEADDR; `}
463 # Authorizes the use of keep-alive packets in a connection
464 new keepalive `{ return SO_KEEPALIVE; `}
466 # Disable the Nagle algorithm and send data as soon as possible, in smaller packets
467 new tcp_nodelay
`{ return TCP_NODELAY; `}
470 # Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event
471 extern class NativeSocketPollValues `{ int `}
473 # Data other than high-priority data may be read without blocking.
474 new pollin
`{ return POLLIN; `}
476 # Normal data may be read without blocking.
477 new pollrdnorm `{ return POLLRDNORM; `}
479 # Priority data may be read without blocking.
480 new pollrdband
`{ return POLLRDBAND; `}
482 # High-priority data may be read without blocking.
483 new pollpri `{ return POLLPRI; `}
485 # Normal data may be written without blocking.
486 new pollout
`{ return POLLOUT; `}
488 # Equivalent to POLLOUT
489 new pollwrnorm `{ return POLLWRNORM; `}
491 # Priority data may be written.
492 new pollwrband
`{ return POLLWRBAND; `}
494 # An error has occurred on the device or stream.
496 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
497 new pollerr `{ return POLLERR; `}
499 # The device has been disconnected.
501 # This event and POLLOUT are mutually-exclusive; a stream can never be
502 # writable if a hangup has occurred. However, this event and POLLIN,
503 # POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive.
505 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
506 new pollhup
`{ return POLLHUP; `}
508 # The specified fd value is invalid.
510 # This flag is only valid in the revents member; it shall ignored in the events member.
511 new pollnval `{ return POLLNVAL; `}
513 # Combines two NativeSocketPollValues
514 private fun +(other
: NativeSocketPollValues): NativeSocketPollValues `{
520 # Get network host entry
521 fun gethostbyname
(name
: NativeString): NativeSocketHostent `{
522 return gethostbyname(name);
525 # Last error raised by `gethostbyname`
526 fun h_errno
: HErrno `{ return h_errno; `}
529 # Error code of `Sys::h_errno
`
530 extern class HErrno `{ int `}
531 # The specified host is unknown
532 fun host_not_found
: Bool `{ return self == HOST_NOT_FOUND; `}
534 # The requested name is valid but does not have an IP address
537 fun no_address: Bool `{ return self == NO_ADDRESS; `}
539 # The requested name is valid byt does not have an IP address
541 # Same as `no_address`.
542 fun no_data
: Bool `{ return self == NO_DATA; `}
544 # A nonrecoverable name server error occurred
545 fun no_recovery: Bool `{ return self == NO_RECOVERY; `}
547 # A temporary error occurred on an authoritative name server, try again later
548 fun try_again
: Bool `{ return self == TRY_AGAIN; `}
552 if host_not_found then
553 return "The specified host is unknown"
554 else if no_address then
555 return "The requested name is valid but does not have an IP address"
556 else if no_recovery then
557 return "A nonrecoverable name server error occurred"
558 else if try_again then
559 return "A temporary error occurred on an authoritative name server, try again later"
561 # This may happen if another call was made to `gethostbyname
`
562 # before we fetch the error code.
563 return "Unknown error on `gethostbyname
`"