scope: refuse `&x` where x is a local variable
[nit.git] / lib / socket / socket_c.nit
index ee6173e..13b4049 100644 (file)
@@ -35,7 +35,7 @@ in "C" `{
        #include <netinet/tcp.h>
 `}
 
-# Wrapper for the data structure PollFD used for polling on a socket
+# Wrapper for the data structure used for polling on a socket
 class PollFD
        super FinalizableOnce
 
@@ -45,18 +45,20 @@ class PollFD
        # A collection of the events to be watched
        var events: Array[NativeSocketPollValues]
 
-       init(pid: Int, events: Array[NativeSocketPollValues])
+       # Create a PollFD object from NativePollFD informations
+       init from_poll_values(pid: Int, events: Array[NativeSocketPollValues])
        do
                assert events.length >= 1
-               self.events = events
 
                var events_in_one = events[0]
 
-               for i in [1 .. events.length-1] do
+               for i in [1 .. events.length - 1] do
                        events_in_one += events[i]
                end
 
-               self.poll_struct = new NativeSocketPollFD(pid, events_in_one)
+               var poll_struct = new NativeSocketPollFD(pid, events_in_one)
+
+               init(poll_struct, events)
        end
 
        # Reads the response and returns an array with the type of events that have been found
@@ -103,8 +105,10 @@ private extern class NativeSocketPollFD `{ struct pollfd * `}
        `}
 end
 
+# Native C socket
 extern class NativeSocket `{ int* `}
 
+       # Create a new C socket
        new socket(domain: NativeSocketAddressFamilies, socketType: NativeSocketTypes, protocol: NativeSocketProtocolFamilies) `{
                int ds = socket(domain, socketType, protocol);
                if(ds == -1){
@@ -115,21 +119,25 @@ extern class NativeSocket `{ int* `}
                return d;
        `}
 
+       # Free the socket
        fun destroy `{ free(self); `}
 
+       # Close the socket in both read/write
        fun close: Int `{ return close(*self); `}
 
+       # Get the FD related to `self`
        fun descriptor: Int `{ return *self; `}
 
-       fun gethostbyname(n: String): NativeSocketHostent import String.to_cstring `{ return gethostbyname(String_to_cstring(n)); `}
-
+       # Connect to another open socket
+       #
+       # SEE: C documentation for more details on the `connect` operation
        fun connect(addrIn: NativeSocketAddrIn): Int `{
                return connect(*self, (struct sockaddr*)addrIn, sizeof(*addrIn));
        `}
 
-       fun write(buffer: String): Int
-       import String.to_cstring, String.length `{
-               return write(*self, (char*)String_to_cstring(buffer), String_length(buffer));
+       # Write `length` bytes from `buffer`
+       fun write(buffer: CString, length: Int): Int `{
+               return write(*self, buffer, length);
        `}
 
        # Write `value` as a single byte
@@ -138,14 +146,9 @@ extern class NativeSocket `{ int* `}
                return write(*self, &byt, 1);
        `}
 
-       fun read: String import NativeString.to_s_with_length, NativeString.to_s_with_copy `{
-               static char c[1024];
-               int n = read(*self, c, 1023);
-               if(n < 0) {
-                       return NativeString_to_s_with_length("",0);
-               }
-               c[n] = 0;
-               return NativeString_to_s_with_copy(c);
+       # Read `length` bytes into `buffer`, returns the number of bytes read
+       fun read(buffer: CString, length: Int): Int `{
+               return read(*self, buffer, length);
        `}
 
        # Sets an option for the socket
@@ -159,8 +162,12 @@ extern class NativeSocket `{ int* `}
                return 1;
        `}
 
+       # Bind the socket to a local address
+       #
+       # SEE: C documentation for more details on the bind operation
        fun bind(addrIn: NativeSocketAddrIn): Int `{ return bind(*self, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
 
+       # Prepare for listening to incoming connections
        fun listen(size: Int): Int `{ return listen(*self, size); `}
 
        # Checks if the buffer is ready for any event specified when creating the pollfd structure
@@ -171,6 +178,15 @@ extern class NativeSocket `{ int* `}
                return filedesc.check_response(result)
        end
 
+       # Poll this socket with `POLLHUP|POLLERR`
+       #
+       # A return value of 0 means there is no errors.
+       fun poll_hup_err: Int `{
+               struct pollfd fd = {*self, POLLHUP|POLLERR, 0};
+               int res = poll(&fd, 1, 0);
+               return res;
+       `}
+
        # Call to the poll function of the C socket
        #
        # Signature:
@@ -201,6 +217,9 @@ extern class NativeSocket `{ int* `}
                return ptr;
        `}
 
+       # Accept a new connection on `self`
+       #
+       # Require the socket to be first bound and listening for connections
        fun accept: nullable SocketAcceptResult
        do
                var addrIn = new NativeSocketAddrIn
@@ -209,7 +228,7 @@ extern class NativeSocket `{ int* `}
                return new SocketAcceptResult(s, addrIn)
        end
 
-       # Set wether this socket is non blocking
+       # Set whether this socket is non blocking
        fun non_blocking=(value: Bool) `{
                int flags = fcntl(*self, F_GETFL, 0);
                if (flags == -1) flags = 0;
@@ -223,6 +242,22 @@ extern class NativeSocket `{ int* `}
                }
                fcntl(*self, F_SETFL, flags);
        `}
+
+       # Send `len` bytes from `buf` to `dest_addr`
+       fun sendto(buf: CString, len: Int, flags: Int, dest_addr: NativeSocketAddrIn): Int `{
+               return sendto(*self, buf, len, flags, (struct sockaddr*)dest_addr, sizeof(struct sockaddr_in));
+       `}
+
+       # Receive a message into `buf` of maximum `len` bytes
+       fun recv(buf: CString, len: Int, flags: Int): Int `{
+               return recv(*self, buf, len, flags);
+       `}
+
+       # Receive a message into `buf` of maximum `len` bytes and store sender info into `src_addr`
+       fun recvfrom(buf: CString, len: Int, flags: Int, src_addr: NativeSocketAddrIn): Int `{
+               socklen_t srclen = sizeof(struct sockaddr_in);
+               return recvfrom(*self, buf, len, flags, (struct sockaddr*)src_addr, &srclen);
+       `}
 end
 
 # Result of a call to `NativeSocket::accept`
@@ -235,66 +270,83 @@ class SocketAcceptResult
        var addr_in: NativeSocketAddrIn
 end
 
+# Socket address in the Internet namespace, pointer to a `struct sockaddr_in`
 extern class NativeSocketAddrIn `{ struct sockaddr_in* `}
+
+       # `NULL` pointer
+       new nul `{ return NULL; `}
+
+       # `malloc` a new instance
        new `{
                struct sockaddr_in *sai = NULL;
                sai = malloc(sizeof(struct sockaddr_in));
                return sai;
        `}
 
-       new with_port(port: Int, family: NativeSocketAddressFamilies) `{
-               struct sockaddr_in *sai = NULL;
-               sai = malloc(sizeof(struct sockaddr_in));
-               sai->sin_family = family;
-               sai->sin_port = htons(port);
-               sai->sin_addr.s_addr = INADDR_ANY;
-               return sai;
+       # Set `address` and `family` from `hostent` (to use with `Sys::gethostbyname`)
+       fun fill_from_hostent(hostent: NativeSocketHostent) `{
+               self->sin_family = hostent->h_addrtype;
+               memcpy((char*)&self->sin_addr.s_addr,
+                      (char*)hostent->h_addr,
+                          hostent->h_length);
        `}
 
-       new with_hostent(hostent: NativeSocketHostent, port: Int) `{
-               struct sockaddr_in *sai = NULL;
-               sai = malloc(sizeof(struct sockaddr_in));
-               sai->sin_family = hostent->h_addrtype;
-               sai->sin_port = htons(port);
-               memcpy((char*)&sai->sin_addr.s_addr, (char*)hostent->h_addr, hostent->h_length);
-               return sai;
-       `}
+       # Internet address as then IPV4 numbers-and-dots notation
+       fun address: CString `{ return (char*)inet_ntoa(self->sin_addr); `}
+
+       # Set `address` to `INADDR_ANY`
+       fun address_any `{ self->sin_addr.s_addr = INADDR_ANY; `}
 
-       fun address: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(self->sin_addr)); `}
+       # Set `address` to `INADDR_BROADCAST`
+       fun address_broadcast `{ self->sin_addr.s_addr = INADDR_BROADCAST; `}
 
+       # Address family
        fun family: NativeSocketAddressFamilies `{ return self->sin_family; `}
 
+       # Address family
+       fun family=(value: NativeSocketAddressFamilies) `{ self->sin_family = value; `}
+
+       # Port
        fun port: Int `{ return ntohs(self->sin_port); `}
 
-       fun destroy `{ free(self); `}
+       # Port
+       fun port=(value: Int) `{ self->sin_port = htons(value); `}
 end
 
+# Host entry information, a pointer to a `struct hostent`
 extern class NativeSocketHostent `{ struct hostent* `}
-       private fun native_h_aliases(i: Int): String import NativeString.to_s `{ return NativeString_to_s(self->h_aliases[i]); `}
-
-       private fun native_h_aliases_reachable(i: Int): Bool `{ return (self->h_aliases[i] != NULL); `}
+       private fun native_h_aliases(i: Int): CString `{
+               return self->h_aliases[i];
+       `}
 
+       # Alternative names for the host
        fun h_aliases: Array[String]
        do
-               var i=0
-               var d=new Array[String]
+               var res = new Array[String]
                loop
-                       d.add(native_h_aliases(i))
-                       if native_h_aliases_reachable(i+1) == false then break
-                       i += 1
+                       var ha = native_h_aliases(res.length)
+                       if ha.address_is_null then break
+                       res.add ha.to_s
                end
-               return d
+               return res
        end
 
-       fun h_addr: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(*(struct in_addr*)self->h_addr)); `}
+       # Host IPv4 address
+       fun h_addr: CString `{
+               return (char*)inet_ntoa(*(struct in_addr*)self->h_addr);
+       `}
 
+       # Host address type
        fun h_addrtype: Int `{ return self->h_addrtype; `}
 
+       # Length in bytes of the addresses
        fun h_length: Int `{ return self->h_length; `}
 
-       fun h_name: String import NativeString.to_s `{ return NativeString_to_s(self->h_name); `}
+       # Host name
+       fun h_name: CString `{ return self->h_name; `}
 end
 
+# Time structure, with a microsecond resolution
 extern class NativeTimeval `{ struct timeval* `}
        new (seconds: Int, microseconds: Int) `{
                struct timeval* tv = NULL;
@@ -304,13 +356,20 @@ extern class NativeTimeval `{ struct timeval* `}
                return tv;
        `}
 
+       # Number of seconds recorded
        fun seconds: Int `{ return self->tv_sec; `}
 
+       # Number of microseconds recorded
        fun microseconds: Int `{ return self->tv_usec; `}
 
+       # Destory `self`
        fun destroy `{ free(self); `}
 end
 
+# Structure used to register FDs for a Select
+#
+# FIXME: This should not be Socket-specific
+# FIXME: This is Unix-specific
 extern class NativeSocketSet `{ fd_set* `}
        new `{
                fd_set *f = NULL;
@@ -318,23 +377,31 @@ extern class NativeSocketSet `{ fd_set* `}
                return f;
        `}
 
+       # Add a file descriptor to the set
        fun set(s: NativeSocket) `{ FD_SET(*s, self); `}
 
+       # Check if `s` is in the set
        fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, self); `}
 
+       # Clear the set
        fun zero `{ FD_ZERO(self); `}
 
+       # Remove `s` from the set
        fun clear(s: NativeSocket) `{ FD_CLR(*s, self); `}
 
+       # Free the set
        fun destroy `{ free(self); `}
 end
 
+# Socket observer
 class NativeSocketObserver
        # FIXME this implementation is broken. `reads`, `write` and `except`
        # are boxed objects, passing them to a C function is illegal.
        fun select(max: NativeSocket, reads: nullable NativeSocketSet, write: nullable NativeSocketSet,
                         except: nullable NativeSocketSet, timeout: NativeTimeval): Int `{
-               fd_set *rds, *wts, *exs = NULL;
+               fd_set *rds = NULL,
+                      *wts = NULL,
+                      *exs = NULL;
                struct timeval *tm = NULL;
                if (reads != NULL) rds = (fd_set*)reads;
                if (write != NULL) wts = (fd_set*)write;
@@ -344,16 +411,20 @@ class NativeSocketObserver
        `}
 end
 
+# Socket types
 extern class NativeSocketTypes `{ int `}
+       # STREAM socket, used for sequential writes/reads
        new sock_stream `{ return SOCK_STREAM; `}
+       # DGRAM socket, used for packet-oriented communication
        new sock_dgram `{ return SOCK_DGRAM; `}
+       # RAW socket, access raw data, without it being handled by the IP stack
        new sock_raw `{ return SOCK_RAW; `}
+       # SEQPACKET, packet-oriented communication with guarantees in packet order
        new sock_seqpacket `{ return SOCK_SEQPACKET; `}
 end
 
+# Socket families
 extern class NativeSocketAddressFamilies `{ int `}
-       new af_null `{ return 0; `}
-
        # Unspecified
        new af_unspec `{ return AF_UNSPEC; `}
 
@@ -381,21 +452,43 @@ extern class NativeSocketAddressFamilies `{ int `}
        # IPv6
        new af_inet6 `{ return AF_INET6; `}
 
+       # Maximum identifier for socket families
        new af_max `{ return AF_MAX; `}
 end
 
+# Socket protocol families
 extern class NativeSocketProtocolFamilies `{ int `}
-       new pf_null `{ return 0; `}
+       # Unspecified
        new pf_unspec `{ return PF_UNSPEC; `}
+
+       # Local socket
        new pf_local `{ return PF_LOCAL; `}
+
+       # Unix socket
        new pf_unix `{ return PF_UNIX; `}
+
+       # Internet (IPv4) socket
        new pf_inet `{ return PF_INET; `}
+
+       # SNA (IBM) socket
        new pf_sna `{ return PF_SNA; `}
+
+       # DECnet socket
        new pf_decnet `{ return PF_DECnet; `}
+
+       # Routing tables control
        new pf_route `{ return PF_ROUTE; `}
+
+       # Novell internet protocol
        new pf_ipx `{ return PF_IPX; `}
+
+       # Key management protocol
        new pf_key `{ return PF_KEY; `}
+
+       # Internet (IPv6) socket
        new pf_inet6 `{ return PF_INET6; `}
+
+       # Maximum identifier for socket families
        new pf_max `{ return PF_MAX; `}
 end
 
@@ -482,3 +575,52 @@ extern class NativeSocketPollValues `{ int `}
                return self | other;
        `}
 end
+
+redef class Sys
+       # Get network host entry
+       fun gethostbyname(name: CString): NativeSocketHostent `{
+               return gethostbyname(name);
+       `}
+
+       # Last error raised by `gethostbyname`
+       fun h_errno: HErrno `{ return h_errno; `}
+end
+
+# Error code of `Sys::h_errno`
+extern class HErrno `{ int `}
+       # The specified host is unknown
+       fun host_not_found: Bool `{ return self == HOST_NOT_FOUND; `}
+
+       # The requested name is valid but does not have an IP address
+       #
+       # Same as `no_data`.
+       fun no_address: Bool `{ return self == NO_ADDRESS; `}
+
+       # The requested name is valid byt does not have an IP address
+       #
+       # Same as `no_address`.
+       fun no_data: Bool `{ return self == NO_DATA; `}
+
+       # A nonrecoverable name server error occurred
+       fun no_recovery: Bool `{ return self == NO_RECOVERY; `}
+
+       # A temporary error occurred on an authoritative name server, try again later
+       fun try_again: Bool `{ return self == TRY_AGAIN; `}
+
+       redef fun to_s
+       do
+               if host_not_found then
+                       return "The specified host is unknown"
+               else if no_address then
+                       return "The requested name is valid but does not have an IP address"
+               else if no_recovery then
+                       return "A nonrecoverable name server error occurred"
+               else if try_again then
+                       return "A temporary error occurred on an authoritative name server, try again later"
+               else
+                       # This may happen if another call was made to `gethostbyname`
+                       # before we fetch the error code.
+                       return "Unknown error on `gethostbyname`"
+               end
+       end
+end