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 gethostbyname
(n
: String): NativeSocketHostent import String.to_cstring
`{ return gethostbyname(String_to_cstring(n)); `}
126 fun connect(addrIn: NativeSocketAddrIn): Int `{
127 return connect
(*self, (struct sockaddr
*)addrIn
, sizeof
(*addrIn
));
130 fun write(buffer: String): Int
131 import String.to_cstring, String.length `{
132 return write
(*self, (char
*)String_to_cstring(buffer
), String_length(buffer
));
135 # Write `value
` as a single byte
136 fun write_byte(value: Int): Int `{
137 unsigned char byt
= (unsigned char
)value
;
138 return write
(*self, &byt
, 1);
141 fun read: String import NativeString.to_s_with_length, NativeString `{
142 char
*c
= new_NativeString
(1024);
143 int n
= read
(*self, c
, 1023);
145 return NativeString_to_s_with_length("",0);
148 return NativeString_to_s_with_length(c
, n
);
151 # Sets an option for the socket
153 # Returns `true` on success.
154 fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int): Bool `{
155 int err
= setsockopt
(*self, level
, option_name
, &option_value
, sizeof
(int
));
162 fun bind(addrIn: NativeSocketAddrIn): Int `{ return bind(*self, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
164 fun listen
(size
: Int): Int `{ return listen(*self, size); `}
166 # Checks if the buffer is ready for any event specified when creating the pollfd structure
167 fun socket_poll(filedesc: PollFD, timeout: Int): Array[NativeSocketPollValues]
169 var result = native_poll(filedesc.poll_struct, timeout)
171 return filedesc.check_response(result)
174 # Call to the poll function of the C socket
177 # int poll(struct pollfd fds[], nfds_t nfds, int timeout);
179 # Official documentation of the poll function:
181 # The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors.
182 # For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events.
183 # The number of pollfd structures in the fds array is specified by nfds.
184 # The poll() function shall identify those file descriptors on which an application can read or write data, or on which certain events have occurred.
185 # The fds argument specifies the file descriptors to be examined and the events of interest for each file descriptor.
186 # It is a pointer to an array with one member for each open file descriptor of interest.
187 # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
188 # OR'ing a combination of the pollfd flags.
189 private fun native_poll(filedesc: NativeSocketPollFD, timeout: Int): Int `{
190 int poll_return
= poll
(filedesc
, 1, timeout
);
194 private fun native_accept(addr_in: NativeSocketAddrIn): NativeSocket `{
195 socklen_t s
= sizeof
(struct sockaddr
);
196 int socket
= accept
(*self, (struct sockaddr
*)addr_in
, &s
);
197 if (socket
== -1) return NULL;
199 int
*ptr
= malloc
(sizeof
(int
));
204 fun accept: nullable SocketAcceptResult
206 var addrIn = new NativeSocketAddrIn
207 var s = native_accept(addrIn)
208 if s.address_is_null then return null
209 return new SocketAcceptResult(s, addrIn)
212 # Set wether this socket is non blocking
213 fun non_blocking=(value: Bool) `{
214 int flags
= fcntl
(*self, F_GETFL, 0);
215 if (flags
== -1) flags
= 0;
218 flags
= flags
| O_NONBLOCK;
219 } else if (flags
& O_NONBLOCK) {
220 flags
= flags
- O_NONBLOCK;
224 fcntl
(*self, F_SETFL, flags
);
228 # Result of a call to `NativeSocket::accept
`
229 class SocketAcceptResult
232 var socket: NativeSocket
234 # Address of the remote client
235 var addr_in: NativeSocketAddrIn
238 extern class NativeSocketAddrIn `{ struct sockaddr_in* `}
240 struct sockaddr_in *sai = NULL;
241 sai = malloc(sizeof(struct sockaddr_in));
245 new with_port
(port
: Int, family
: NativeSocketAddressFamilies) `{
246 struct sockaddr_in *sai = NULL;
247 sai = malloc(sizeof(struct sockaddr_in));
248 sai->sin_family = family;
249 sai->sin_port = htons(port);
250 sai->sin_addr.s_addr = INADDR_ANY;
254 new with_hostent
(hostent
: NativeSocketHostent, port
: Int) `{
255 struct sockaddr_in *sai = NULL;
256 sai = malloc(sizeof(struct sockaddr_in));
257 sai->sin_family = hostent->h_addrtype;
258 sai->sin_port = htons(port);
259 memcpy((char*)&sai->sin_addr.s_addr, (char*)hostent->h_addr, hostent->h_length);
263 fun address
: String import NativeString.to_s
`{ return NativeString_to_s((char*)inet_ntoa(self->sin_addr)); `}
265 fun family: NativeSocketAddressFamilies `{ return self->sin_family; `}
267 fun port
: Int `{ return ntohs(self->sin_port); `}
269 fun destroy `{ free(self); `}
272 extern class NativeSocketHostent `{ struct hostent* `}
273 private fun native_h_aliases(i: Int): String import NativeString.to_s `{ return NativeString_to_s(self->h_aliases[i]); `}
275 private fun native_h_aliases_reachable
(i
: Int): Bool `{ return (self->h_aliases[i] != NULL); `}
277 fun h_aliases: Array[String]
280 var d=new Array[String]
282 d.add(native_h_aliases(i))
283 if native_h_aliases_reachable(i+1) == false then break
289 fun h_addr: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(*(struct in_addr*)self->h_addr)); `}
291 fun h_addrtype
: Int `{ return self->h_addrtype; `}
293 fun h_length: Int `{ return self->h_length; `}
295 fun h_name
: String import NativeString.to_s
`{ return NativeString_to_s(self->h_name); `}
298 extern class NativeTimeval `{ struct timeval* `}
299 new (seconds
: Int, microseconds
: Int) `{
300 struct timeval* tv = NULL;
301 tv = malloc(sizeof(struct timeval));
302 tv->tv_sec = seconds;
303 tv->tv_usec = microseconds;
307 fun seconds
: Int `{ return self->tv_sec; `}
309 fun microseconds: Int `{ return self->tv_usec; `}
311 fun destroy
`{ free(self); `}
314 extern class NativeSocketSet `{ fd_set* `}
317 f = malloc(sizeof(fd_set));
321 fun set
(s
: NativeSocket) `{ FD_SET(*s, self); `}
323 fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, self); `}
325 fun zero
`{ FD_ZERO(self); `}
327 fun clear(s: NativeSocket) `{ FD_CLR(*s, self); `}
329 fun destroy
`{ free(self); `}
332 class NativeSocketObserver
333 # FIXME this implementation is broken. `reads
`, `write
` and `except
`
334 # are boxed objects, passing them to a C function is illegal.
335 fun select(max: NativeSocket, reads: nullable NativeSocketSet, write: nullable NativeSocketSet,
336 except: nullable NativeSocketSet, timeout: NativeTimeval): Int `{
337 fd_set
*rds
, *wts
, *exs
= NULL;
338 struct timeval
*tm
= NULL;
339 if (reads
!= NULL) rds
= (fd_set
*)reads
;
340 if (write
!= NULL) wts
= (fd_set
*)write
;
341 if (except
!= NULL) exs
= (fd_set
*)except
;
342 if (timeout
!= NULL) tm
= (struct timeval
*)timeout
;
343 return select
(*max
, rds
, wts
, exs
, tm
);
347 extern class NativeSocketTypes `{ int `}
348 new sock_stream
`{ return SOCK_STREAM; `}
349 new sock_dgram `{ return SOCK_DGRAM; `}
350 new sock_raw
`{ return SOCK_RAW; `}
351 new sock_seqpacket `{ return SOCK_SEQPACKET; `}
354 extern class NativeSocketAddressFamilies `{ int `}
355 new af_null `{ return 0; `}
358 new af_unspec
`{ return AF_UNSPEC; `}
360 # Local to host (pipes)
361 new af_unix `{ return AF_UNIX; `}
363 # For backward compatibility
364 new af_local
`{ return AF_LOCAL; `}
366 # Internetwork: UDP, TCP, etc.
367 new af_inet `{ return AF_INET; `}
370 new af_sna
`{ return AF_SNA; `}
373 new af_decnet `{ return AF_DECnet; `}
375 # Internal Routing Protocol
376 new af_route
`{ return AF_ROUTE; `}
378 # Novell Internet Protocol
379 new af_ipx `{ return AF_IPX; `}
382 new af_inet6
`{ return AF_INET6; `}
384 new af_max `{ return AF_MAX; `}
387 extern class NativeSocketProtocolFamilies `{ int `}
388 new pf_null `{ return 0; `}
389 new pf_unspec
`{ return PF_UNSPEC; `}
390 new pf_local `{ return PF_LOCAL; `}
391 new pf_unix
`{ return PF_UNIX; `}
392 new pf_inet `{ return PF_INET; `}
393 new pf_sna
`{ return PF_SNA; `}
394 new pf_decnet `{ return PF_DECnet; `}
395 new pf_route
`{ return PF_ROUTE; `}
396 new pf_ipx `{ return PF_IPX; `}
397 new pf_key
`{ return PF_KEY; `}
398 new pf_inet6 `{ return PF_INET6; `}
399 new pf_max
`{ return PF_MAX; `}
402 # Level on which to set options
403 extern class NativeSocketOptLevels `{ int `}
405 # Dummy for IP (As defined in C)
406 new ip
`{ return IPPROTO_IP;`}
408 # Control message protocol
409 new icmp `{ return IPPROTO_ICMP;`}
412 new tcp
`{ return IPPROTO_TCP; `}
414 # Socket level options
415 new socket `{ return SOL_SOCKET; `}
418 # Options for socket, use with setsockopt
419 extern class NativeSocketOptNames `{ int `}
421 # Enables debugging information
422 new debug `{ return SO_DEBUG; `}
424 # Authorizes the broadcasting of messages
425 new broadcast
`{ return SO_BROADCAST; `}
427 # Authorizes the reuse of the local address
428 new reuseaddr `{ return SO_REUSEADDR; `}
430 # Authorizes the use of keep-alive packets in a connection
431 new keepalive
`{ return SO_KEEPALIVE; `}
433 # Disable the Nagle algorithm and send data as soon as possible, in smaller packets
434 new tcp_nodelay `{ return TCP_NODELAY; `}
437 # Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event
438 extern class NativeSocketPollValues `{ int `}
440 # Data other than high-priority data may be read without blocking.
441 new pollin `{ return POLLIN; `}
443 # Normal data may be read without blocking.
444 new pollrdnorm
`{ return POLLRDNORM; `}
446 # Priority data may be read without blocking.
447 new pollrdband `{ return POLLRDBAND; `}
449 # High-priority data may be read without blocking.
450 new pollpri
`{ return POLLPRI; `}
452 # Normal data may be written without blocking.
453 new pollout `{ return POLLOUT; `}
455 # Equivalent to POLLOUT
456 new pollwrnorm
`{ return POLLWRNORM; `}
458 # Priority data may be written.
459 new pollwrband `{ return POLLWRBAND; `}
461 # An error has occurred on the device or stream.
463 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
464 new pollerr
`{ return POLLERR; `}
466 # The device has been disconnected.
468 # This event and POLLOUT are mutually-exclusive; a stream can never be
469 # writable if a hangup has occurred. However, this event and POLLIN,
470 # POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive.
472 # This flag is only valid in the revents bitmask; it shall be ignored in the events member.
473 new pollhup `{ return POLLHUP; `}
475 # The specified fd value is invalid.
477 # This flag is only valid in the revents member; it shall ignored in the events member.
478 new pollnval
`{ return POLLNVAL; `}
480 # Combines two NativeSocketPollValues
481 private fun +(other: NativeSocketPollValues): NativeSocketPollValues `{