X-Git-Url: http://nitlanguage.org diff --git a/lib/socket/socket_c.nit b/lib/socket/socket_c.nit index ba76860..13b4049 100644 --- a/lib/socket/socket_c.nit +++ b/lib/socket/socket_c.nit @@ -35,7 +35,7 @@ in "C" `{ #include `} -# 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 @@ -87,13 +89,13 @@ end private extern class NativeSocketPollFD `{ struct pollfd * `} # File descriptor - fun fd: Int `{ return recv->fd; `} + fun fd: Int `{ return self->fd; `} # List of events to be watched - fun events: Int `{ return recv->events; `} + fun events: Int `{ return self->events; `} # List of events received by the last poll function - fun revents: Int `{ return recv->revents; `} + fun revents: Int `{ return self->revents; `} new (pid: Int, events: NativeSocketPollValues) `{ struct pollfd *poll = malloc(sizeof(struct pollfd)); @@ -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,47 +119,56 @@ extern class NativeSocket `{ int* `} return d; `} - fun destroy `{ free(recv); `} - - fun close: Int `{ return close(*recv); `} + # Free the socket + fun destroy `{ free(self); `} - fun descriptor: Int `{ return *recv; `} + # Close the socket in both read/write + fun close: Int `{ return close(*self); `} - fun gethostbyname(n: String): NativeSocketHostent import String.to_cstring `{ return gethostbyname(String_to_cstring(n)); `} + # Get the FD related to `self` + fun descriptor: Int `{ return *self; `} + # Connect to another open socket + # + # SEE: C documentation for more details on the `connect` operation fun connect(addrIn: NativeSocketAddrIn): Int `{ - return connect(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn)); + return connect(*self, (struct sockaddr*)addrIn, sizeof(*addrIn)); `} - fun write(buffer: String): Int - import String.to_cstring, String.length `{ - return write(*recv, (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); `} - fun read: String import NativeString.to_s_with_length `{ - static char c[1024]; - int n = read(*recv, c, 1024); - if(n < 0) { - return NativeString_to_s_with_length("",0); - } - char* ret = malloc(n + 1); - memcpy(ret, c, n); - ret[n] = '\0'; - return NativeString_to_s_with_length(ret, n); + # Write `value` as a single byte + fun write_byte(value: Int): Int `{ + unsigned char byt = (unsigned char)value; + return write(*self, &byt, 1); + `} + + # 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 - fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int) `{ - int err = setsockopt(*recv, level, option_name, &option_value, sizeof(int)); + # + # Returns `true` on success. + fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int): Bool `{ + int err = setsockopt(*self, level, option_name, &option_value, sizeof(int)); if(err != 0){ - perror("Error on setsockopts: "); - exit(1); + return 0; } + return 1; `} - fun bind(addrIn: NativeSocketAddrIn): Int `{ return bind(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn)); `} + # 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)); `} - fun listen(size: Int): Int `{ return listen(*recv, size); `} + # 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 fun socket_poll(filedesc: PollFD, timeout: Int): Array[NativeSocketPollValues] @@ -165,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: @@ -187,7 +209,7 @@ extern class NativeSocket `{ int* `} private fun native_accept(addr_in: NativeSocketAddrIn): NativeSocket `{ socklen_t s = sizeof(struct sockaddr); - int socket = accept(*recv, (struct sockaddr*)addr_in, &s); + int socket = accept(*self, (struct sockaddr*)addr_in, &s); if (socket == -1) return NULL; int *ptr = malloc(sizeof(int)); @@ -195,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 @@ -203,9 +228,9 @@ 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(*recv, F_GETFL, 0); + int flags = fcntl(*self, F_GETFL, 0); if (flags == -1) flags = 0; if (value) { @@ -215,7 +240,23 @@ extern class NativeSocket `{ int* `} } else { return; } - fcntl(*recv, F_SETFL, flags); + 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 @@ -229,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: 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(recv->sin_addr)); `} + # Set `address` to `INADDR_BROADCAST` + fun address_broadcast `{ self->sin_addr.s_addr = INADDR_BROADCAST; `} - fun family: NativeSocketAddressFamilies `{ return recv->sin_family; `} + # Address family + fun family: NativeSocketAddressFamilies `{ return self->sin_family; `} - fun port: Int `{ return ntohs(recv->sin_port); `} + # Address family + fun family=(value: NativeSocketAddressFamilies) `{ self->sin_family = value; `} - fun destroy `{ free(recv); `} + # Port + fun port: Int `{ return ntohs(self->sin_port); `} + + # 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(recv->h_aliases[i]); `} - - private fun native_h_aliases_reachable(i: Int): Bool `{ return (recv->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*)recv->h_addr)); `} + # Host IPv4 address + fun h_addr: CString `{ + return (char*)inet_ntoa(*(struct in_addr*)self->h_addr); + `} - fun h_addrtype: Int `{ return recv->h_addrtype; `} + # Host address type + fun h_addrtype: Int `{ return self->h_addrtype; `} - fun h_length: Int `{ return recv->h_length; `} + # 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(recv->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; @@ -298,13 +356,20 @@ extern class NativeTimeval `{ struct timeval* `} return tv; `} - fun seconds: Int `{ return recv->tv_sec; `} + # Number of seconds recorded + fun seconds: Int `{ return self->tv_sec; `} - fun microseconds: Int `{ return recv->tv_usec; `} + # Number of microseconds recorded + fun microseconds: Int `{ return self->tv_usec; `} - fun destroy `{ free(recv); `} + # 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; @@ -312,23 +377,31 @@ extern class NativeSocketSet `{ fd_set* `} return f; `} - fun set(s: NativeSocket) `{ FD_SET(*s, recv); `} + # Add a file descriptor to the set + fun set(s: NativeSocket) `{ FD_SET(*s, self); `} - fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, recv); `} + # Check if `s` is in the set + fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, self); `} - fun zero `{ FD_ZERO(recv); `} + # Clear the set + fun zero `{ FD_ZERO(self); `} - fun clear(s: NativeSocket) `{ FD_CLR(*s, recv); `} + # Remove `s` from the set + fun clear(s: NativeSocket) `{ FD_CLR(*s, self); `} - fun destroy `{ free(recv); `} + # 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; @@ -338,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; `} @@ -375,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 @@ -473,6 +572,55 @@ extern class NativeSocketPollValues `{ int `} # Combines two NativeSocketPollValues private fun +(other: NativeSocketPollValues): NativeSocketPollValues `{ - return recv | other; + 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