/*
* net.c
*
- * Created on: Mar 8, 2013
- * Author: volpol
- * Modifed:
- * 2013-05-31 support bind address, statics no more [uw]
+ * Copyright (c) 2019, Urban Wallasch
+ * BSD 3-Clause License, see LICENSE file for more details.
+ *
+ * Collection of functions to help create simple IPv4 or IPv6 based
+ * TCP or UDP servers and clients. See net.h for further information.
+ *
+ * Build with -DNET_ERR to enable error logging to stderr.
+ * Build with -DNET_DBG to enable error and debug logging to stderr.
+ *
*/
-// Needed for inet_aton() and herror():
-#define _DEFAULT_SOURCE
-
+#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-
+#include <unistd.h>
#include <poll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
#include <netdb.h>
+#include <arpa/inet.h>
-#include <unistd.h>
+#include "net.h"
+
+
+/* Size of copy buffer in sendfile_tm(): */
+#define SENDFILE_TM_BUFSIZE 64000
+
+/* Retry attempts for zero byte write in sendfile_tm(): */
+#define SENDFILE_TM_RETRY_0 5
+
+
+/* Logging support macros and helper functions: */
+
+#if defined(NET_DBG) && !defined(NET_ERR)
+#define NET_ERR
+#endif
+
+#ifdef NET_ERR
+
+/* Log helper function: create "host:port" string from socket address: */
+static char *addr2str(const struct sockaddr *addr, socklen_t addrlen,
+ char *buf, size_t bufsize) {
+ char host[64], svc[10];
+ int err;
+ err = getnameinfo(addr, addrlen, host, sizeof host, svc, sizeof svc,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (err == 0)
+ snprintf(buf, bufsize, "%s:%s", host, svc);
+ else
+ snprintf(buf, bufsize, "<?>:<?>");
+ return buf;
+}
+
+#define ADDR2STR(a_,l_,b_,s_) char (b_)[(s_)]; addr2str((a_),(l_),(b_),(s_))
+
+#define NETLOG_MSG(fi_,fn_,ln_,...) do { \
+ fprintf(stderr,"%s:%s@%d: ",fi_,fn_,ln_); \
+ fprintf(stderr,__VA_ARGS__); \
+ } while(0)
+
+#define NETLOG_ERR(e_,...) do { \
+ char ebuf[100]; \
+ NETLOG_MSG(__FILE__,__func__,__LINE__,__VA_ARGS__); \
+ fprintf(stderr,": %s\n",net_strerror(e_,ebuf,sizeof ebuf));\
+ } while(0)
+
+#ifdef NET_DBG
+#define NETLOG_DBG(...) do { \
+ NETLOG_MSG(__FILE__,__func__,__LINE__,"DEBUG: "__VA_ARGS__);\
+ fprintf(stderr,"\n"); \
+ } while(0)
+#else
+#define NETLOG_DBG(...)
+#endif
+
+#else
+#define ADDR2STR(...)
+#define NETLOG_ERR(...)
+#define NETLOG_DBG(...)
+#endif /* NET_ERR */
+
+
+/* Helper function to bind a client socket to local node address: */
+static int net_bind_local(int sock, const char *addr, int st, int af ) {
+ /* Only try to bind if actually requested! */
+ if (addr == NULL || *addr == '\0' || *addr == '*')
+ return 0;
+
+ int err;
+ struct addrinfo hints, *info;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = af;
+ hints.ai_socktype = st;
+ hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
+ err = getaddrinfo(addr, NULL, &hints, &info);
+ if (err != 0) {
+ NETLOG_ERR(err, "getaddrinfo(%s)", addr);
+ return err;
+ }
+ for (struct addrinfo *ai = info; ai != NULL; ai = ai->ai_next) {
+ ADDR2STR(ai->ai_addr, ai->ai_addrlen, stra, 100);
+ err = bind(sock, ai->ai_addr, ai->ai_addrlen);
+ if (err != 0) {
+ NETLOG_ERR(EAI_SYSTEM, "bind(%d, %s)", sock, stra);
+ continue;
+ }
+ else {
+ NETLOG_DBG("bind(%d, %s): Ok", sock, stra);
+ }
+ break;
+ }
+ freeaddrinfo(info);
+ return err < 0 ? EAI_SYSTEM : 0;
+}
+
+/* Check, if an error return code indicates system error: */
+int net_issyserr(int errnum) {
+ return (errnum == EAI_SYSTEM);
+}
+
+/* Copy textual description of last error to user supplied buffer: */
+char *net_strerror(int errnum, char *buf, size_t len) {
+ if (errnum >= 0)
+ snprintf(buf, len, "Success");
+ else if (net_issyserr(errnum))
+ strerror_r(errno, buf, len);
+ else
+ snprintf(buf, len, gai_strerror(errnum));
+ return buf;
+}
+
+/* Create and bind a new listener socket: */
+int net_open_server(const char *addr, const char *svc, int st, int af) {
+ int err, sock = -1;
+ struct addrinfo hints, *info;
+
+ addr = addr && *addr && *addr != '*' ? addr : NULL;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = af;
+ hints.ai_socktype = st;
+ hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE;
+ err = getaddrinfo(addr, svc, &hints, &info);
+ if (err != 0) {
+ NETLOG_ERR(err, "getaddrinfo(%s,%s)", addr, svc);
+ return err;
+ }
+ for (struct addrinfo *ai = info; ai != NULL; ai = ai->ai_next) {
+ int set = 1;
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0) {
+ NETLOG_ERR(EAI_SYSTEM, "socket()");
+ continue;
+ }
+ err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof set);
+ if (err != 0) {
+ NETLOG_ERR(EAI_SYSTEM, "setsockopt(%d)", sock);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ ADDR2STR(ai->ai_addr, ai->ai_addrlen, stra, 100);
+ err = bind(sock, ai->ai_addr, ai->ai_addrlen);
+ if (err != 0) {
+ NETLOG_ERR(EAI_SYSTEM, "bind(%d, %s)", sock, stra);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ else {
+ NETLOG_DBG("bind(%d, %s): Ok", sock, stra);
+ }
+ break;
+ }
+ freeaddrinfo(info);
+ if (sock < 0)
+ return EAI_SYSTEM;
+ if (st == SOCK_STREAM) {
+ err = listen(sock, 10);
+ if (err != 0) {
+ NETLOG_ERR(EAI_SYSTEM, "listen(%d)", sock);
+ close(sock);
+ return EAI_SYSTEM;
+ }
+ }
+ return sock;
+}
-#include "log.h"
-
-
-int net_open_client( const char *host, unsigned short port, const char *bind_addr )
-{
- int sock = -1;
- struct sockaddr_in server;
- struct hostent *he = NULL;
-
- //WHOAMI;
- DPRINT( "%s:%d [%s]\n", host, port, bind_addr ? bind_addr : "*" );
- if ( 0 > ( sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) )
- return perror( "socket()" ), -1;
- server.sin_port = htons( port );
- server.sin_family = AF_INET;
- if ( NULL == ( he = gethostbyname( host ) ) )
- {
- herror( "gethostbyname()" );
- goto ERROUT;
- }
- if ( NULL != bind_addr )
- {
- struct sockaddr_in lob;
- lob.sin_family = AF_INET;
- if ( 0 != strcmp( bind_addr, "*" ) )
- {
- if ( 0 == strcmp( bind_addr, "localhost" ) )
- bind_addr = "127.0.0.1";
- lob.sin_family = AF_INET;
- if ( 0 == inet_aton( bind_addr, (struct in_addr *)&lob.sin_addr.s_addr ) )
- {
- fprintf( stderr, "inet_aton() failed\n" );
- goto ERROUT;
- }
- }
- else
- lob.sin_addr.s_addr = INADDR_ANY;
- if ( 0 > bind( sock, (const struct sockaddr*)&lob, sizeof lob ) )
- {
- perror( "bind()" );
- goto ERROUT;
- }
- }
- server.sin_addr.s_addr = *((in_addr_t*)he->h_addr_list[0]);
- if ( 0 != connect( sock, (struct sockaddr *)&server, sizeof server ) )
- {
- perror( "connect()" );
- goto ERROUT;
- }
- return sock;
-ERROUT:
- close( sock );
- return -1;
+/* Create and connect a new socket: */
+int net_open_client(const char *host, const char *svc, const char *addr, int st, int af) {
+ int err, sock = -1;
+ struct addrinfo hints, *info;
+
+ host = host && *host ? host : NULL;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = af;
+ hints.ai_socktype = st;
+ hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
+ err = getaddrinfo(host, svc, &hints, &info);
+ if (err != 0) {
+ NETLOG_ERR(err, "getaddrinfo(%s,%s)", host, svc);
+ return err;
+ }
+ for (struct addrinfo *ai = info; ai != NULL; ai = ai->ai_next) {
+ int set = 1;
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0) {
+ NETLOG_ERR(EAI_SYSTEM, "socket()");
+ continue;
+ }
+ err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof set);
+ if (err != 0) {
+ NETLOG_ERR(EAI_SYSTEM, "setsockopt(%d)", sock);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ err = net_bind_local(sock, addr, ai->ai_socktype, ai->ai_family);
+ if (err != 0) {
+ /* Errors already logged in net_bind_local()! */
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ ADDR2STR(ai->ai_addr, ai->ai_addrlen, stra, 100);
+ err = connect(sock, ai->ai_addr, ai->ai_addrlen);
+ if (err != 0) {
+ NETLOG_ERR(EAI_SYSTEM, "connect(%d, %s)", sock, stra);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ else {
+ NETLOG_DBG("connect(%d, %s): Ok", sock, stra);
+ }
+ break;
+ }
+ freeaddrinfo(info);
+ if (sock < 0)
+ return EAI_SYSTEM;
+ return sock;
}
-int net_accept( int sock )
-{
- WHOAMI;
- int conn = -1;
- struct pollfd pe;
- int res;
-
- pe.fd = sock;
- pe.events = POLLIN;
- pe.revents = 0;
- if( (0 < (res = poll (&pe, 1, 1000))) && pe.revents == POLLIN){
- if ( 0 > ( conn = accept( sock, NULL, NULL ) ) )
- perror( "accept()" );
- else
- {
- DPRINT( "accepted conn: %d\n", conn );
- }
- } else {
- return -1;
- }
- return conn;
+/* Close socket: */
+int net_close(int sock) {
+ int err;
+ err = shutdown(sock, SHUT_RDWR);
+ if (err < 0) {
+ /* This error is not forwarded to caller! */
+ NETLOG_DBG("shutdown(%d): %s", sock, strerror(errno));
+ }
+ err = close(sock);
+ if (err < 0) {
+ NETLOG_ERR(EAI_SYSTEM, "close(%d)", sock);
+ }
+ return err < 0 ? EAI_SYSTEM : 0;
}
-void net_close( int conn )
-{
- WHOAMI;
- if ( 0 <= conn )
- {
- DPRINT( "shutting down conn: %d\n", conn );
- shutdown( conn, SHUT_RDWR );
- close( conn );
- }
+/* Wait for and accept a TCP connection request: */
+int tcp_accept(int sock, int timeout) {
+ int r;
+ struct pollfd pfd;
+
+ pfd.fd = sock;
+ pfd.events = POLLIN;
+ r = poll(&pfd, 1, timeout);
+ if (r == 1) {
+ if (pfd.revents & (POLLIN | POLLERR | POLLHUP)) {
+ /* In case of POLLERR or POLLHUP we let accept() catch the error! */
+ struct sockaddr_storage addr;
+ struct sockaddr *paddr = (struct sockaddr *)&addr;
+ socklen_t addrlen = sizeof addr;
+ r = accept(sock, paddr, &addrlen);
+ if (r < 0) {
+ r = EAI_SYSTEM;
+ }
+ else {
+ ADDR2STR(paddr, addrlen, stra, 100);
+ NETLOG_DBG("accept(%d) -> (%d, %s): Ok", sock, r, stra);
+ }
+ }
+ else if (pfd.revents & POLLNVAL) {
+ errno = EBADF;
+ r = EAI_SYSTEM;
+ }
+ else {
+ NETLOG_DBG("This should never happen! (r=%d, revents=0x%04X)",
+ r, (unsigned)pfd.revents);
+ errno = EIO;
+ r = EAI_SYSTEM;
+ }
+ }
+ else if (r == 0) {
+ errno = ETIME;
+ r = EAI_SYSTEM;
+ }
+ else if (r < 0) {
+ r = EAI_SYSTEM;
+ }
+ if (r < 0) {
+ NETLOG_ERR(r, "tcp_accept(%d)", sock);
+ }
+ return r;
}
-int net_open_server( unsigned short port, const char *bind_addr )
-{
- int sock;
- int reuse = 1;
- struct sockaddr_in lob;
-
- //WHOAMI;
- DPRINT( "%d [%s]\n", port, bind_addr ? bind_addr : "*" );
- if ( 0 > ( sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) )
- return perror( "socket()" ), -1;
- if ( 0 != setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse ) )
- {
- perror( "setsockopt()" );
- goto ERROUT;
- }
- lob.sin_family = AF_INET;
- lob.sin_port = htons( port );
- if ( NULL != bind_addr && 0 != strcmp( bind_addr, "*" ) )
- {
- if ( 0 == strcmp( bind_addr, "localhost" ) )
- bind_addr = "127.0.0.1";
- lob.sin_family = AF_INET;
- if ( 0 == inet_aton( bind_addr, (struct in_addr *)&lob.sin_addr.s_addr ) )
- {
- fprintf( stderr, "inet_aton() failed\n" );
- goto ERROUT;
- }
- }
- else
- lob.sin_addr.s_addr = INADDR_ANY;
- if ( 0 > bind( sock, (const struct sockaddr*)&lob, sizeof lob ) )
- {
- perror( "bind()" );
- goto ERROUT;
- }
- if ( 0 > listen( sock, 0 ) )
- {
- perror( "listen()" );
- goto ERROUT;
- }
- return sock;
-ERROUT:
- close( sock );
- return -1;
+/* Read from socket or file descriptor: */
+ssize_t recvfrom_tm(int fd, void *buf, size_t len, int flags,
+ struct sockaddr *addr, socklen_t *alen, int timeout) {
+ ssize_t r;
+ struct pollfd pfd;
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ r = poll(&pfd, 1, timeout);
+ if (r == 1) {
+ if (pfd.revents & (POLLIN | POLLERR | POLLHUP)) {
+ /* In case of POLLERR or POLLHUP we let recv() catch the error! */
+ r = recvfrom(fd, buf, len, flags | MSG_DONTWAIT, addr, alen);
+ if (r < 0 && errno == ENOTSOCK) {
+ NETLOG_DBG("recvfrom_tm(%d): not a socket, trying read()", fd);
+ r = read(fd, buf, len);
+ if (r >= 0 && alen != NULL)
+ *alen = 0;
+ }
+ r = r < 0 ? EAI_SYSTEM : r;
+ }
+ else if (pfd.revents & POLLNVAL) {
+ errno = EBADF;
+ r = EAI_SYSTEM;
+ }
+ else {
+ NETLOG_DBG("This should never happen! (r=%zd, revents=0x%04X)",
+ r, (unsigned)pfd.revents);
+ errno = EIO;
+ r = EAI_SYSTEM;
+ }
+ }
+ else if (r == 0) {
+ errno = ETIME;
+ r = EAI_SYSTEM;
+ }
+ else if (r < 0) {
+ r = EAI_SYSTEM;
+ }
+ if (r < 0) {
+ NETLOG_ERR(r, "recvfrom_tm(%d)", fd);
+ }
+ else {
+ NETLOG_DBG("recvfrom_tm(%d): %zd bytes read", fd, r);
+ }
+ return r;
}
+
+/* Write to socket or file descriptor: */
+ssize_t sendto_tm(int fd, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t alen, int timeout) {
+ ssize_t r;
+ struct pollfd pfd;
+
+ pfd.fd = fd;
+ pfd.events = POLLOUT;
+ r = poll(&pfd, 1, timeout);
+ if (r == 1) {
+ if (pfd.revents & (POLLOUT | POLLERR | POLLHUP)) {
+ /* In case of POLLERR or POLLHUP we let send() catch the error! */
+ r = sendto(fd, buf, len, flags | MSG_DONTWAIT | MSG_NOSIGNAL, addr, alen);
+ if (r < 0 && errno == ENOTSOCK) {
+ NETLOG_DBG("sendto_tm(%d): not a socket, trying write()", fd);
+ r = write(fd, buf, len);
+ }
+ r = r < 0 ? EAI_SYSTEM : r;
+ }
+ else if (pfd.revents & POLLNVAL) {
+ errno = EBADF;
+ r = EAI_SYSTEM;
+ }
+ else {
+ NETLOG_DBG("This should never happen! (r=%zd, revents=0x%04X)",
+ r, (unsigned)pfd.revents);
+ errno = EIO;
+ r = EAI_SYSTEM;
+ }
+ }
+ else if (r == 0) {
+ errno = ETIME;
+ r = EAI_SYSTEM;
+ }
+ else if (r < 0) {
+ r = EAI_SYSTEM;
+ }
+ if (r < 0) {
+ NETLOG_ERR(r, "sendto_tm(%d)", fd);
+ }
+ else {
+ NETLOG_DBG("sendto_tm(%d): %zd bytes written", fd, r);
+ }
+ return r;
+}
+
+/* Copy count bytes from in_fd to out_fd: */
+int64_t sendfile_tm(int out_fd, int in_fd, int64_t count, int timeout) {
+ int64_t ret, total;
+ char *buf;
+
+ buf = malloc(SENDFILE_TM_BUFSIZE);
+ if (buf == NULL)
+ return EAI_SYSTEM;
+
+ for (total = 0, ret = 0; ret == 0; ) {
+ int retry_zero_wr;
+ ssize_t nrd, nwr, n;
+ n = SENDFILE_TM_BUFSIZE;
+ if (count >= 0 && n > count - total)
+ n = count - total;
+ nrd = read_tm(in_fd, buf, n, timeout);
+ if (nrd < 0) {
+ if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
+ continue;
+ ret = nrd;
+ continue;
+ }
+ else if (nrd == 0) { /* all done */
+ ret = total;
+ break; /* Avoid infinite loop on empty input! */
+ }
+ retry_zero_wr = SENDFILE_TM_RETRY_0;
+ nwr = 0;
+ while (nwr < nrd && ret == 0) {
+ n = write_tm(out_fd, buf + nwr, nrd - nwr, timeout);
+ if (n < 0) {
+ if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
+ continue;
+ ret = n;
+ continue;
+ }
+ else if (n == 0) {
+ if (retry_zero_wr-- <= 0) {
+ errno = ENOSPC;
+ ret = EAI_SYSTEM;
+ NETLOG_ERR(ret, "sendfile_tm(%d,%d)", out_fd, in_fd);
+ continue;
+ }
+ NETLOG_DBG("sendfile_tm(%d,%d): zero write, retry %d",
+ out_fd, in_fd, SENDFILE_TM_RETRY_0 - retry_zero_wr);
+ }
+ else {
+ retry_zero_wr = SENDFILE_TM_RETRY_0;
+ }
+ nwr += n;
+ }
+ total += nwr;
+ }
+ free(buf);
+ return ret;
+}
+
/*
* net.h
*
- * Created on: Mar 8, 2013
- * Author: volpol
+ * Copyright (c) 2019, Urban Wallasch
+ * BSD 3-Clause License, see LICENSE file for more details.
+ *
+ * Collection of functions to help create simple IPv4 or IPv6 based
+ * TCP or UDP servers and clients.
+ *
+ * XXX_open_server - functions and macros to set up a simple server
+ *
+ * XXX_open_client - functions and macros to connect to a network host
+ *
+ * net_close - close a network socket
+ *
+ * tcp_accept - accept a TCP connection request on listener socket
+ *
+ * net_issyserr - determine, if return code indicates system error
+ *
+ * net_strerror - get textual error message
+ *
+ * recvfrom_tm
+ * recv_tm
+ * read_tm - functions and macros to read with timeout
+ *
+ * sendto_tm
+ * send_tm
+ * write_tm - functions and macros to write with timeout
+ *
+ * sendfile_tm - copy data from one file descriptor to another
+ *
*/
#ifndef NET_H_
#define NET_H_
-int net_accept( int sock );
-int net_open_server( unsigned short port, const char *bind_addr );
-int net_open_client( const char *host, unsigned short port, const char *bind_addr );
-void net_close( int conn );
+#ifdef cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+
+/*
+ * Test if an error return code indicates a system level error.
+ *
+ * Returns either 1, in which case the actual error code can be found
+ * in errno, or 0, which means errnum should be checked against the
+ * list of EAI_XXX constants defined in netdb.h.
+ *
+ * errnum - <int> a negative value returned by any function from net.h
+ *
+ * Note: So far the only functions which may return non-system errors
+ * are those of the XXX_open_server() and XXX_open_client() varieties.
+ * In other words, it is safe to assume that all other functions
+ * declared in net.h in case of failure store their error codes in
+ * errno.
+ *
+ * See also: net_strerror.
+ */
+extern int net_issyserr(int errnum);
+
+
+/*
+ * Copy textual description of last error to user supplied buffer.
+ *
+ * Returns a pointer to the buffer.
+ *
+ * errnum - <int> error code returned by any of the above functions
+ * buf - <char*> user supplied buffer to store the error message
+ * len - <size_t> maximum number of characters buf can hold
+ *
+ * Note: Depending on the actual value of errnum this function will
+ * return a message based on either strerror_r() or gai_strerror().
+ */
+extern char *net_strerror(int errnum, char *buf, size_t len);
+
+
+/*
+ * Create a new socket with the specified characteristics, bind it to
+ * the local node address and, in case of SOCK_STREAM, start listening
+ * for connection requests which can be accepted using tcp_accept().
+ *
+ * Returns the bound listener socket, or a negative value on error.
+ *
+ * addr - <string> local node address (numerical or name); NULL for any
+ * svc - <string> service (port number or service name)
+ * st - <int> socket type; one of SOCK_STREAM or SOCK_DGRAM
+ * af - <int> address family; one of AF_UNSPEC, AF_INET, AF_INET6
+ *
+ * Note: Address family AF_INET6 will allow mapped IPv4 connections.
+ * AF_UNSPEC will cause the first suitable bind address to be used,
+ * the sort order is determined by the host configuration, e.g. in
+ * /etc/gai.conf for glibc.
+ */
+extern int net_open_server(const char *addr, const char *svc, int st, int af);
+
+/*
+ * Shorthand macros for socket type and address family permutations:
+ */
+#define tcp_open_server(a_,s_) net_open_server((a_),(s_),SOCK_STREAM,AF_UNSPEC)
+#define tcp4_open_server(a_,s_) net_open_server((a_),(s_),SOCK_STREAM,AF_INET)
+#define tcp6_open_server(a_,s_) net_open_server((a_),(s_),SOCK_STREAM,AF_INET6)
+#define udp_open_server(a_,s_) net_open_server((a_),(s_),SOCK_DGRAM,AF_UNSPEC)
+#define udp4_open_server(a_,s_) net_open_server((a_),(s_),SOCK_DGRAM,AF_INET)
+#define udp6_open_server(a_,s_) net_open_server((a_),(s_),SOCK_DGRAM,AF_INET6)
+
+
+/*
+ * Create a new socket, optionally bind it to a local node address,
+ * associate it with a random ephemeral port and connect it to the
+ * specified host and service.
+ *
+ * Returns the connected socket, or a negative value on error.
+ *
+ * host - <string> remote node address (numerical or host name)
+ * svc - <string> remote service (port number or service name)
+ * addr - <string> local node address to bind to, or NULL for none
+ * st - <int> socket type; one of SOCK_STREAM or SOCK_DGRAM
+ * af - <int> address family; one of AF_UNSPEC, AF_INET, AF_INET6
+ */
+extern int net_open_client(const char *host, const char *svc, const char *addr, int st, int af);
+
+/*
+ * Shorthand macros for socket type and address family permutations:
+ */
+#define tcp_open_client(h_,s_,a_) net_open_client((h_),(s_),(a_),SOCK_STREAM,AF_UNSPEC)
+#define tcp4_open_client(h_,s_,a_) net_open_client((h_),(s_),(a_),SOCK_STREAM,AF_INET)
+#define tcp6_open_client(h_,s_,a_) net_open_client((h_),(s_),(a_),SOCK_STREAM,AF_INET6)
+#define udp_open_client(h_,s_,a_) net_open_client((h_),(s_),(a_),SOCK_DGRAM,AF_UNSPEC)
+#define udp4_open_client(h_,s_,a_) net_open_client((h_),(s_),(a_),SOCK_DGRAM,AF_INET)
+#define udp6_open_client(h_,s_,a_) net_open_client((h_),(s_),(a_),SOCK_DGRAM,AF_INET6)
+
+
+/*
+ * Shutdown and close the specified socket.
+ *
+ * Returns 0 on success, or a negative value on error.
+ *
+ * Note: Errors returned by shutdown() are silently ignored and not
+ * forwarded.
+ *
+ * sock - <int> socket to close
+ */
+extern int net_close(int sock);
+
+
+/*
+ * Wait at most timeout milliseconds for a TCP connection request on
+ * a listener socket to arrive and accept any such request.
+ *
+ * Returns a newly created socket associated with the accepted TCP
+ * connection, or a negative value on error.
+ *
+ * sock - <int> TCP listener socket
+ * timeout - <int> timeout in milliseconds
+ *
+ * Note: When the time-out expires a negative value is returned and
+ * errno is set to ETIME. A negative value for timeout will cause
+ * tcp_accept() to wait indefinitely. Set timeout to zero for immediate
+ * return. The function will return prematurely when interrupted by a
+ * signal.
+ */
+extern int tcp_accept(int sock, int timeout);
+
+
+/*
+ * Read from file or socket descriptor with timeout.
+ *
+ * Returns the number of bytes received, or a negative value on error.
+ *
+ * fd - <int> file or socket descriptor to read from
+ * buf - <char*> user supplied buffer to store the received message
+ * len - <size_t> maximum number of bytes to store in buf
+ * flags - <int> additional flags to pass, see: man recv
+ * addr - <struct sockaddr*> see: man recvfrom
+ * alen - <socklen_t*> see: man recvfrom
+ * timeout - <int> timeout in milliseconds
+ *
+ * Note: A zero return value indicates the equivalent of an EOF condition.
+ * When reading from a socket, the MSG_DONTWAIT flag is added in the
+ * underlying call to recvfrom(). When the time-out expires, a negative
+ * value is returned and errno is set to ETIME. A negative value for
+ * timeout will cause indefinite wait. Set timeout to zero for immediate
+ * return. The function will return prematurely when interrupted by a
+ * signal. When reading from a non-socket file descriptor flags, addr
+ * and alen are ignored, and performance may be slightly inferior
+ * compared to a plain read() call.
+ */
+extern ssize_t recvfrom_tm(int fd, void *buf, size_t len, int flags,
+ struct sockaddr *addr, socklen_t *alen, int timeout);
+/*
+ * Shorthand macros to closely match recv() and read() prototypes:
+ */
+#define recv_tm(d_,b_,l_,f_,t_) recvfrom_tm((d_),(b_),(l_),(f_),NULL,NULL,(t_))
+#define read_tm(d_,b_,l_,t_) recvfrom_tm((d_),(b_),(l_),0,NULL,NULL,(t_))
+
+
+/*
+ * Write to file or socket descriptor with timeout.
+ *
+ * Returns the number of bytes sent, or a negative value on error.
+ *
+ * fd - <int> file or socket descriptor to read from
+ * buf - <char*> user supplied buffer to store the received message
+ * len - <size_t> maximum number of bytes to store in buf
+ * flags - <int> additional flags to pass, see: man send
+ * addr - <struct sockaddr*> see: man sendto
+ * alen - <socklen_t*> see: man sendto
+ * timeout - <int> timeout in milliseconds
+ *
+ * Note: A zero return value indicates the equivalent of an EOF condition.
+ * When writing to a socket, the MSG_DONTWAIT and MSG_NOSIGNAL flags are
+ * added in the underlying call to sendto(). When the time-out expires,
+ * a negative value is returned and errno is set to ETIME. A negative
+ * value for timeout will cause indefinite wait. Set timeout to zero
+ * for immediate return. The function will return prematurely when
+ * interrupted by a signal. When writing to a non-socket file descriptor
+ * flags, addr and alen are ignored, and performance may be slightly
+ * inferior compared to a plain write() call.
+ */
+extern ssize_t sendto_tm(int fd, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t alen, int timeout);
+/*
+ * Shorthand macros to closely match send() and write() prototypes:
+ */
+#define send_tm(d_,b_,l_,f_,t_) sendto_tm((d_),(b_),(l_),(f_),NULL,0,(t_))
+#define write_tm(d_,b_,l_,t_) sendto_tm((d_),(b_),(l_),0,NULL,0,(t_))
+
+
+/*
+ * Copy up to count bytes of data from in_fd to out_fd with timeout.
+ *
+ * Returns the total number of bytes copied, or a negative value on error.
+ *
+ * out_fd - <int> file or socket descriptor to write to
+ * in_fd - <int> file or socket descriptor to read from
+ * count - <int64_t> maximum number of bytes to copy
+ * timeout - <int> timeout in milliseconds
+ *
+ * Note: The timeout is applied to each individual internal read or write
+ * operation. If zero bytes were written, up to 5 attempts to retransmit
+ * are made. Thus the timeout may add up multiple times.
+ *
+ * If count is negative, copying takes place until the end of the input
+ * stream is hit. If count is zero a single read operation with a count
+ * of zero is performed.
+ */
+extern int64_t sendfile_tm(int out_fd, int in_fd, int64_t count, int timeout);
+
+
+#ifdef cplusplus
+}
+#endif
-#endif /* NET_H_ */
+#endif /*ndef NET_H_*/