This is netdial, a small opinionated C utility library to ease some of the
tedious parts of programming with the BSD sockets API while providing sane
defaults.
- Support for Unix and IP (TCP, UDP) sockets.
- Usage of address strings to describe sockets.
- Sane defaults for socket file descriptors:
- Non-blocking (
O_NONBLOCK). - Close on exec (
O_CLOEXEC).
- Non-blocking (
- Uses modern best practices internally:
getaddrinfo+getnameinfo,accept4where available, etc.
- Providing a simplified, convenience API for creating, initiating and accepting socket connections.
- Encouraging good network programming practices (e.g. non-blocking file descriptors as default).
- Completely replacing the BSD sockets API.
- Supporting
SOCK_DGRAMfor Unix sockets. - Supporting protocol families other than IP or Unix sockets.
Socket addresses are represented as strings of the form
<type>:<node>[:<service>], for example tcp6:perezdecastro.org:www-http.
The <type> field is mandatory and determines the address family and
connection type. It must be one of unix, unixp, tcp, udp, tcp4,
udp4, tcp6, or udp6.
For unix and unixp addresses the <node> field must be the socket path
and the <service> field must be omitted. The unixp type choses a
SOCK_SEQPACKET instead of SOCK_STREAM as the protocol. Unix sockets with
protocol SOCK_DGRAM are not supported (see non-goals above).
For tcp and udp sockets the <node> field is the address where to listen
or to connect to. The unversioned <type> names will choose either IPv4 or
v6 depending on name resolution and what is supported by your system, while
the versioned ones can be used to explicitly choose the IP version to use.
When specifying IP addresses directly, IPv6 addresses must be specified in
between square brackets. IPv6 zone names (and indexes) are supported with the
usual syntax, using a percent sign as separator.
Note that the <node> field may be left empty, in which case the address
string represents “any address”, which is equivalent to 0.0.0.0 and :: for
IPv4 and v6 addresses, respectively. This is particularly convenient when
passing addresses to netannounce() for creating listening
sockets.
int netdial(const char *address, int flags);Creates a socket connected to address (see Address
Strings), with the given flags (see Socket
Flags).
Returns the socket file descriptor. On error, returns -1 and sets the
errno variable appropriately.
int netannounce(const char *address, int flags, int backlog);Creates a socket listening at address (see Address
Strings), with the given flags (see Socket
Flags). The backlog argument defines the maximum amount of
pending connections to queue unaccepted e.g. using netaccept().
Returns the socket file descriptor. On error, returns -1 and sets the
errno variable appropriately.
int netaccept(int fd, int flags, char **remoteaddress);Takes the next connection in the queue of pending connections for the fd
socket, creates a new socket file descriptor for it with the given flags
(see Socket Flags), and returns it.
The fd socket must be bound and listening for connections as created by
netannounce(), and use a connection oriented protocol (unix,
unixp, tcp4, tcp6).
If the remoteaddress argument is not NULL, it is set to the address of the
remote peer. This is the same address returned by netaddress()
when used with NDremote. The caller is responsible of calling free() on
the returned value.
Returns the socket file descriptor for the accepted socket connection. On
error, returns -1 and sets the errno variable appropriately.
int nethangup(int fd, int how);
enum { NDclose, NDread, NDwrite, NDrdwr }; /* how */Hangs up the fd socket connection, partially or completely depending on the
value of the how argument:
NDclose: Completely closes the socket, which cannot be used afterwards.NDread: Half-closes the socket for reading; writing data and manipulating socket state is still possible.NDwrite: Half-closes the socket for writing; reading data and manipulating socket state is still possible.NDrdwr: Closes the socket for data transfer; only manipulating socket state is possible.
int netaddress(int fd, int kind, char **address);
enum { NDlocal, NDremote }; /* kind */Obtain an address associated with the fd socket, depending on the value of
the kind argument:
NDlocal: Local socket address.NDremote: Remote peer socket address.
The address argument must not be NULL and will be used to store the
requested address. The caller is responsible of calling free() on the
returned value.
Returns 0 on success. On error, returns -1 and sets the errno variable
appropriately.
enum {
NDdefault,
/* Common socket flags. */
NDblocking,
NDexeckeep,
NDdebug,
NDreuseaddr,
NDreuseport,
/* UDP socket flags. */
NDbroadcast,
/* TCP socket flags. */
NDkeepalive,
/* Unix socket flags. */
NDpasscred,
NDpassec,
};Functions that create socket file descriptors receive a flags argument,
which is a bitwise or (C operator |) of the following values:
NDdefault: Use default options for the socket (non-blocking, close-on-exec).NDblocking: Do no set the socket in non-blocking mode; reading or writing may block.NDexeckeep: Do not set the close-on-exec flag; the socket will be usable after the program callsexec*().NDdebug: Enable socket debugging.NDreuseaddr: Set theSO_REUSEADDRsocket option.NDreuseport: Set theSO_REUSEPORTsocket option.NDbroadcast: For UDP sockets, allow sending data to broadcast addresses.NDkeepalive: For TCP sockets, enable sensing keep-alive messages.NDpasscred: For Unix sockets, enable receiving theSCM_CREDENTIALScontrol message.NDpassec: For Unix sockets, enable receiving theSCM_SECURITYcontrol message.