CXING Sockets API

This chapter forms an integral part of module "The Networking Module" - should "The Input/Output Module" be implemneted, this chapter along with any chapter constituting part of "The Input/Output Module" must be implemented in their entirity.

This module depend on module "The Input/Output Module", should this module be implemented, module "The Input/Output Module" must also be implemented.

Additional Error Number Requirements

If the platform on which a cxing program runs uses separate namespace for either the identifier or the value (or both) of error numbers for socket-specific errors than the errno codes of other system-diagnosed errors, the implementation shall map any socket-specific error codes to the namespace for errno codes.

Note: For example, on Windows, the Winsock 2 API have the WSA prefix to error values such as WSAEWOULDBLOCK, WSAEINTR, etc. The implementation must map those to EWOULDBLOCK, EINTR, etc. when they cast them into null for failure returns.

General

[Socket(GenericFile): subr socket(dom, typ, proto)] := {
  method send(str, flags),
  method recv(len, flags), // returns a string object.
  method sendto(str, flags, peer),
  method recvfrom(len, flags, peer), // returns a string object

  method shutdown(how),
  method bind(name),
  method connect(peer),
  method listen(backlog),

  // returns a socket, optionally placing the address of the peer in `addr`.
  method accept(addr?),

  method getconfig(k),
  method setconfig(k, v),
  method __copy__(),
  method __final__(),
}

The parameters peer and name for sendto, recvfrom, and bind, as well as the return value from accept are 'socket address' objects described below.

The methods for a socket object return null that uncasts to errno codes on failures. The correspondence between the socket methods and POSIX socket API are as follow:

Method Name POSIX API Return Value on Success Special Return Values
`send` `send` The number of bytes sent, -
`recv` `recv` The received data as a string object, 0 on EOF.
`sendto` `sendto` The number of bytes sent, -
`recvfrom` `recvfrom` The received data as a string object, 0 on EOF.
`shutdown` `shutdown` 0 -
`bind` `bind` 0 -
`connect` `connect` 0 -
`listen` `listen` 0 -
`accept` `accept` A socket for interacting with the accepted connection. -

The implementation shall define at least the following integer constants corresponding respectively to those in the POSIX API:

The getconfig and setconfig methods of a socket object can be used to inspect and configure socket options and properties.

Socket Options and Properties

The sockname and peername properties are the socket addresses of the socket object and its peer respectively, and are retrieved using the getconfig method.

The options on the other hand consist of a 'level' and a 'name', separated by a solidus /. Implementations shall support the following options, and may support additional options, Boolean options are of type long and may assume the value of either true or false.

Option Name, Configuration Value Type,
`SOL_SOCKET/SO_ACCEPTCONN` (read-only) Boolean
`SOL_SOCKET/SO_BROADCAST` Boolean
`SOL_SOCKET/SO_DEBUG` Boolean
`SOL_SOCKET/SO_DOMAIN` (read-only) Socket Domain Enumeration, (new in POSIX-2024)
`SOL_SOCKET/SO_DONTROUTE` Boolean
`SOL_SOCKET/SO_ERROR` (read-only, cleared on read) `long`
`SOL_SOCKET/SO_KEEPALIVE` Boolean
`SOL_SOCKET/SO_LINGER` Socket Linger Object
`SOL_SOCKET/SO_OOBINLINE` (read-only) Boolean (always `true`, see rationale below).
`SOL_SOCKET/SO_PROTOCOL` Boolean (new in POSIX-2024)
`SOL_SOCKET/SO_RCVBUF` `long`
`SOL_SOCKET/SO_RCVLOWAT` `long`
`SOL_SOCKET/SO_RCVTIMEO` `double` interpreted in seconds.
`SOL_SOCKET/SO_REUSEADDR` Boolean
`SOL_SOCKET/SO_SNDBUF` `long`
`SOL_SOCKET/SO_SNDLOWAT` `long`
`SOL_SOCKET/SO_SNDTIMEO` `double` interpreted in seconds.
`SOL_SOCKET/SO_TYPE` (read-only) Socket Type Enumeration
`IPPROTO_IPV6/IPV6_JOIN_GROUP` `ipv6_mreq`
`IPPROTO_IPV6/IPV6_LEAVE_GROUP` `ipv6_mreq`
`IPPROTO_IPV6/IPV6_MULTICAST_HOPS` `long`
`IPPROTO_IPV6/IPV6_MULTICAST_IF` `ulong`
`IPPROTO_IPV6/IPV6_MULTICAST_LOOP` Boolean
`IPPROTO_IPV6/IPV6_UNICAST_HOPS` `long`
`IPPROTO_IPV6/IPV6_V6ONLY` Boolean
`IPPROTO_TCP/TCP_NODELAY` Boolean

The newer RFC-9293 on the TCP protocol has advised against the use of the 'urgent' flag in new applications. Although support for compatibility with older applications that does use out-of-band from implementations are allowed, CXING is not one of those older language to begin with, so we're omitting its support, and requiring that the socket subroutine to set the SO_OOBINLINE option for all of its newly created sockets.

The setsync method of a socket (which is inherited from generic files) shall be implemented using (i.e. on top of) the TCP_NODELAY option.

Socket Address and Other Miscellaneous Types

Certain POSIX socket APIs expect pointers to structures that require language bindings. Implementations may represent these structures as actual structures in memory, and expose their members through the __get__ and the __set__ methods, by reading from and writing to fields in the structures directly. All subroutines that creates these so-called DirectStructFieldAccess types shall initialize them to all-bit-zero before returning them.

[DirectStructFieldAccess] := {
  method __get__(k),
  method __set__(k, v),
  method __initset__(k, v),
  method __copy__(),
  method __final__(),
}

[sockaddr(DirectStructFieldAccess): subr sockaddr()]

The __set__ and the __get__ methods of socket address structure types shall convert sin_port, sin_addr.s_addr, and sin6_port between host byte order (setter argument and getter return) and network byte order (underlying data backing) when called; the fields sin_addr and sin6_addr shall be 4-byte and 16-byte string objects respectively.

[sock_linger(DirectStructFieldAccess): subr sock_linger()]
[ipv6_mreq(DirectStructFieldAccess): subr ipv6_mreq()]

Hostname-Address Resolution

[subr getaddrinfo(hostname, service, family, socktype, protocol, flags)] := {
  [method get(ind)] := { // Note this is distinct from the `__get__` method!
    method get(k), // Note similarly the distinction!
  },
}

The getaddrinfo function finds (resolves) the socket addresses of a service indicated by service residing at the host named by hostname. These 2 arguments shall be string objects.

The family, socktype, protocol, and the flags parameters hints to the resolver the kind of socket address being sought. They corresponds to the respective fields in the POSIX addrinfo structure to the hint argument to the POSIX getaddrinfo function. If no hinting is needed, they may be specified as zero (The AF_UNSPEC address family has a numerical value of 0 as mandated by the standard).

On success, the function returns an object with a method get, which receives a single integer argument, as (0-based) index to access the addrinfo the socket address info structures, which may be consulted to create sockets to initiate or receive connections. The index to this method begins at 0, and ends at the first index where the method returns null.

On error, one of the EAI_* error codes, or in the case of EAI_SYSTEM, one of the errno codes shall be casted into a null and returned.

The addrinfo address contains another get method, and the following attributes are accessible through this get method:

Note: because ai_flags is the hint input, it's not part of mandated readable output; since the result is returned in the form of array objects, there's no need for ai_next field.

Implementation Note: The C API for getting address info uses the addrinfo data structure type to return addresses informations in the form of chained lists - although sufficient for applications to iterate over the list, this API design isn't ideal for higher-level languages such as cxing, where there's better idioms for iterators. It is very desirable to reuse the underlying data backing for the cxing API binding directly, one for efficiency reasons, and also avoid the cumbersomeness to error-check evey step of a potential API that actually 'converts' the C backing to an array of named tuples - a get method for attribute access distinct from the __get__ method for object property access is decided on for this.

subr getnameinfo(sockaddr, flags);

The getnameinfo function shall return an object with 2 member fields:

The implementation shall define at least the following integer constants corresponding respectively to those in the POSIX API:

Synchronous Multiplexing

subr pollset(capacity) := {
  method __copy__(),
  method __final__(),
  method __initset__(),
  method poll(timeout),
  method revents(index),
}

The pollset function creates a set for holding input/output handles for 'polling'. Polling allows the program to determine which ones of the set of handles are ready to perform which types of IO operations.

The created set shall be capable of holding capacity IO handles, supplied as the key argument (left-side of the colon in the brace form of object-definition notation), for events specified in the value argument (right-side correspondingly) of the __initset__ method.

The poll method examines the IO handles in the set for IO or exceptional events, and returns the number of IO handles ready for IO operations when they are or when timeout non-negative milliseconds has elapsed (i.e. if timeout is negative, poll may wait indefinitely).

The order in which IO handles are specified in __initset__ is significant, and is used as the zero-based ordinal specified to the revents method to query the occurrence of IO and exceptional events.

The implementation shall define at least the following integer constants corresponding respectively to those in the POSIX API: