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 fun write
(buffer
: String): Int
129 import String.to_cstring
, String.length
`{
130 return write(*self, (char*)String_to_cstring(buffer), String_length(buffer));
133 # Write `value` as a single byte
134 fun write_byte
(value
: Int): Int `{
135 unsigned char byt = (unsigned char)value;
136 return write(*self, &byt, 1);
139 fun read
: String import NativeString.to_s_with_length
, NativeString `{
140 char *c = new_NativeString(1024);
141 int n = read(*self, c, 1023);
143 return NativeString_to_s_with_length("",0);
146 return NativeString_to_s_with_length(c, n);
149 # Sets an option for the socket
151 # Returns `true` on success.
152 fun setsockopt
(level
: NativeSocketOptLevels, option_name
: NativeSocketOptNames, option_value
: Int): Bool `{
153 int err = setsockopt(*self, level, option_name, &option_value, sizeof(int));
160 fun bind
(addrIn
: NativeSocketAddrIn): Int `{ return bind(*self, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
162 fun listen(size: Int): Int `{ return listen(*self, size); `}
164 # Checks if the buffer is ready for any event specified when creating the pollfd structure
165 fun socket_poll
(filedesc
: PollFD, timeout
: Int): Array[NativeSocketPollValues]
167 var result
= native_poll
(filedesc
.poll_struct
, timeout
)
169 return filedesc
.check_response
(result
)
172 # Call to the poll function of the C socket
175 # int poll(struct pollfd fds[], nfds_t nfds, int timeout);
177 # Official documentation of the poll function:
179 # The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.
180 # For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events.
181 # The number of pollfd structures in the fds array is specified by nfds.
182 # The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred.
183 # The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor.
184 # It is a pointer to an array with one member for each open file descriptor of interest.
185 # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
186 # OR'ing a combination of the pollfd flags.
187 private fun native_poll
(filedesc
: NativeSocketPollFD, timeout
: Int): Int `{
188 int poll_return = poll(filedesc, 1, timeout);
192 private fun native_accept
(addr_in
: NativeSocketAddrIn): NativeSocket `{
193 socklen_t s = sizeof(struct sockaddr);
194 int socket = accept(*self, (struct sockaddr*)addr_in, &s);
195 if (socket == -1) return NULL;
197 int *ptr = malloc(sizeof(int));
202 fun accept
: nullable SocketAcceptResult
204 var addrIn
= new NativeSocketAddrIn
205 var s
= native_accept
(addrIn
)
206 if s
.address_is_null
then return null
207 return new SocketAcceptResult(s
, addrIn
)
210 # Set wether this socket is non blocking
211 fun non_blocking
=(value
: Bool) `{
212 int flags = fcntl(*self, F_GETFL, 0);
213 if (flags == -1) flags = 0;
216 flags = flags | O_NONBLOCK;
217 } else if (flags & O_NONBLOCK) {
218 flags = flags - O_NONBLOCK;
222 fcntl(*self, F_SETFL, flags);
226 # Result of a call to `NativeSocket::accept`
227 class SocketAcceptResult
230 var socket
: NativeSocket
232 # Address of the remote client
233 var addr_in
: NativeSocketAddrIn
236 extern class NativeSocketAddrIn `{ struct sockaddr_in* `}
238 struct sockaddr_in
*sai
= NULL;
239 sai
= malloc
(sizeof
(struct sockaddr_in
));
243 new with_port(port: Int, family: NativeSocketAddressFamilies) `{
244 struct sockaddr_in
*sai
= NULL;
245 sai
= malloc
(sizeof
(struct sockaddr_in
));
246 sai-
>sin_family
= family
;
247 sai-
>sin_port
= htons
(port
);
248 sai-
>sin_addr
.s_addr
= INADDR_ANY;
252 new with_hostent(hostent: NativeSocketHostent, port: Int) `{
253 struct sockaddr_in
*sai
= NULL;
254 sai
= malloc
(sizeof
(struct sockaddr_in
));
255 sai-
>sin_family
= hostent-
>h_addrtype
;
256 sai-
>sin_port
= htons
(port
);
257 memcpy
((char
*)&sai-
>sin_addr
.s_addr
, (char
*)hostent-
>h_addr
, hostent-
>h_length
);
261 fun address: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(self->sin_addr)); `}
263 fun family
: NativeSocketAddressFamilies `{ return self->sin_family; `}
265 fun port: Int `{ return ntohs(self->sin_port); `}
267 fun destroy
`{ free(self); `}
270 extern class NativeSocketHostent `{ struct hostent* `}
271 private fun native_h_aliases
(i
: Int): String import NativeString.to_s
`{ return NativeString_to_s(self->h_aliases[i]); `}
273 private fun native_h_aliases_reachable(i: Int): Bool `{ return (self->h_aliases[i] != NULL); `}
275 fun h_aliases
: Array[String]
278 var d
=new Array[String]
280 d
.add
(native_h_aliases
(i
))
281 if native_h_aliases_reachable
(i
+1) == false then break
287 fun h_addr
: String import NativeString.to_s
`{ return NativeString_to_s((char*)inet_ntoa(*(struct in_addr*)self->h_addr)); `}
289 fun h_addrtype: Int `{ return self->h_addrtype; `}
291 fun h_length
: Int `{ return self->h_length; `}
293 fun h_name: String import NativeString.to_s `{ return NativeString_to_s(self->h_name); `}
296 extern class NativeTimeval `{ struct timeval* `}
297 new (seconds: Int, microseconds: Int) `{
298 struct timeval
* tv
= NULL;
299 tv
= malloc
(sizeof
(struct timeval
));
300 tv-
>tv_sec
= seconds
;
301 tv-
>tv_usec
= microseconds
;
305 fun seconds: Int `{ return self->tv_sec; `}
307 fun microseconds
: Int `{ return self->tv_usec; `}
309 fun destroy `{ free(self); `}
312 extern class NativeSocketSet `{ fd_set* `}
315 f
= malloc
(sizeof
(fd_set
));
319 fun set(s: NativeSocket) `{ FD_SET(*s, self); `}
321 fun is_set
(s
: NativeSocket): Bool `{ return FD_ISSET(*s, self); `}
323 fun zero `{ FD_ZERO(self); `}
325 fun clear
(s
: NativeSocket) `{ FD_CLR(*s, self); `}
327 fun destroy `{ free(self); `}
330 class NativeSocketObserver
331 # FIXME this implementation is broken. `reads`, `write` and `except`
332 # are boxed objects, passing them to a C function is illegal.
333 fun select
(max
: NativeSocket, reads
: nullable NativeSocketSet, write
: nullable NativeSocketSet,
334 except
: nullable NativeSocketSet, timeout
: NativeTimeval): Int `{
335 fd_set *rds, *wts, *exs = NULL;
336 struct timeval *tm = NULL;
337 if (reads != NULL) rds = (fd_set*)reads;
338 if (write != NULL) wts = (fd_set*)write;
339 if (except != NULL) exs = (fd_set*)except;
340 if (timeout != NULL) tm = (struct timeval*)timeout;
341 return select(*max, rds, wts, exs, tm);
345 extern class NativeSocketTypes `{ int `}
346 new sock_stream `{ return SOCK_STREAM; `}
347 new sock_dgram
`{ return SOCK_DGRAM; `}
348 new sock_raw `{ return SOCK_RAW; `}
349 new sock_seqpacket
`{ return SOCK_SEQPACKET; `}
352 extern class NativeSocketAddressFamilies `{ int `}
353 new af_null
`{ return 0; `}
356 new af_unspec `{ return AF_UNSPEC; `}
358 # Local to host (pipes)
359 new af_unix
`{ return AF_UNIX; `}
361 # For backward compatibility
362 new af_local `{ return AF_LOCAL; `}
364 # Internetwork: UDP, TCP, etc.
365 new af_inet
`{ return AF_INET; `}
368 new af_sna `{ return AF_SNA; `}
371 new af_decnet
`{ return AF_DECnet; `}
373 # Internal Routing Protocol
374 new af_route `{ return AF_ROUTE; `}
376 # Novell Internet Protocol
377 new af_ipx
`{ return AF_IPX; `}
380 new af_inet6 `{ return AF_INET6; `}
382 new af_max
`{ return AF_MAX; `}
385 extern class NativeSocketProtocolFamilies `{ int `}
386 new pf_null
`{ return 0; `}
387 new pf_unspec `{ return PF_UNSPEC; `}
388 new pf_local
`{ return PF_LOCAL; `}
389 new pf_unix `{ return PF_UNIX; `}
390 new pf_inet
`{ return PF_INET; `}
391 new pf_sna `{ return PF_SNA; `}
392 new pf_decnet
`{ return PF_DECnet; `}
393 new pf_route `{ return PF_ROUTE; `}
394 new pf_ipx
`{ return PF_IPX; `}
395 new pf_key `{ return PF_KEY; `}
396 new pf_inet6
`{ return PF_INET6; `}
397 new pf_max `{ return PF_MAX; `}
400 # Level on which to set options
401 extern class NativeSocketOptLevels `{ int `}
403 # Dummy for IP (As defined in C)
404 new ip `{ return IPPROTO_IP;`}
406 # Control message protocol
407 new icmp
`{ return IPPROTO_ICMP;`}
410 new tcp `{ return IPPROTO_TCP; `}
412 # Socket level options
413 new socket
`{ return SOL_SOCKET; `}
416 # Options for socket, use with setsockopt
417 extern class NativeSocketOptNames `{ int `}
419 # Enables debugging information
420 new debug
`{ return SO_DEBUG; `}
422 # Authorizes the broadcasting of messages
423 new broadcast `{ return SO_BROADCAST; `}
425 # Authorizes the reuse of the local address
426 new reuseaddr
`{ return SO_REUSEADDR; `}
428 # Authorizes the use of keep-alive packets in a connection
429 new keepalive `{ return SO_KEEPALIVE; `}
431 # Disable the Nagle algorithm and send data as soon as possible, in smaller packets
432 new tcp_nodelay
`{ return TCP_NODELAY; `}
435 # Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event
436 extern class NativeSocketPollValues `{ int `}
438 # Data other than high-priority data may be read without blocking.
439 new pollin
`{ return POLLIN; `}
441 # Normal data may be read without blocking.
442 new pollrdnorm `{ return POLLRDNORM; `}
444 # Priority data may be read without blocking.
445 new pollrdband
`{ return POLLRDBAND; `}
447 # High-priority data may be read without blocking.
448 new pollpri `{ return POLLPRI; `}
450 # Normal data may be written without blocking.
451 new pollout
`{ return POLLOUT; `}
453 # Equivalent to POLLOUT
454 new pollwrnorm `{ return POLLWRNORM; `}
456 # Priority data may be written.
457 new pollwrband
`{ return POLLWRBAND; `}
459 # An error has occurred on the device or stream.
461 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
462 new pollerr `{ return POLLERR; `}
464 # The device has been disconnected.
466 # This event and POLLOUT are mutually-exclusive; a stream can never be
467 # writable if a hangup has occurred. However, this event and POLLIN,
468 # POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive.
470 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
471 new pollhup
`{ return POLLHUP; `}
473 # The specified fd value is invalid.
475 # This flag is only valid in the revents member; it shall ignored in the events member.
476 new pollnval `{ return POLLNVAL; `}
478 # Combines two NativeSocketPollValues
479 private fun +(other
: NativeSocketPollValues): NativeSocketPollValues `{
485 # Get network host entry
486 fun gethostbyname
(name
: NativeString): NativeSocketHostent `{
487 return gethostbyname(name);
490 # Last error raised by `gethostbyname`
491 fun h_errno
: HErrno `{ return h_errno; `}
494 # Error code of `Sys::h_errno
`
495 extern class HErrno `{ int `}
496 # The specified host is unknown
497 fun host_not_found
: Bool `{ return self == HOST_NOT_FOUND; `}
499 # The requested name is valid but does not have an IP address
502 fun no_address: Bool `{ return self == NO_ADDRESS; `}
504 # The requested name is valid byt does not have an IP address
506 # Same as `no_address`.
507 fun no_data
: Bool `{ return self == NO_DATA; `}
509 # A nonrecoverable name server error occurred
510 fun no_recovery: Bool `{ return self == NO_RECOVERY; `}
512 # A temporary error occurred on an authoritative name server, try again later
513 fun try_again
: Bool `{ return self == TRY_AGAIN; `}
517 if host_not_found then
518 return "The specified host is unknown"
519 else if no_address then
520 return "The requested name is valid but does not have an IP address"
521 else if no_recovery then
522 return "A nonrecoverable name server error occurred"
523 else if try_again then
524 return "A temporary error occurred on an authoritative name server, try again later"
526 # This may happen if another call was made to `gethostbyname
`
527 # before we fetch the error code.
528 return "Unknown error on `gethostbyname
`"