return buf;
}
+/* 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 && 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 (r == 0) {
+ errno = ETIME;
+ r = EAI_SYSTEM;
+ }
+ else if (r < 0) {
+ r = EAI_SYSTEM;
+ }
+ else if (pfd.revents & POLLNVAL) {
+ errno = EBADF;
+ r = EAI_SYSTEM;
+ }
+ else {
+ NETLOG_DBG("This should never happen! (r=%zd, revents=%hd)", r, pfd.revents);
+ errno = EIO;
+ r = EAI_SYSTEM;
+ }
+ if (r < 0) {
+ NETLOG_ERR(r, "recv_tm(%d)", fd);
+ }
+ else {
+ NETLOG_DBG("recv_tm(%d): %zd bytes read", fd, r);
+ }
+ return r;
+}
+
+/* Write to socket or file descriptor: */
+ssize_t sendto_tm(int fd, 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 && 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 (r == 0) {
+ errno = ETIME;
+ r = EAI_SYSTEM;
+ }
+ else if (r < 0) {
+ r = EAI_SYSTEM;
+ }
+ else if (pfd.revents & POLLNVAL) {
+ errno = EBADF;
+ r = EAI_SYSTEM;
+ }
+ else {
+ NETLOG_DBG("This should never happen! (r=%zd, revents=%hd)", r, pfd.revents);
+ errno = EIO;
+ r = EAI_SYSTEM;
+ }
+ if (r < 0) {
+ NETLOG_ERR(r, "send_tm(%d)", fd);
+ }
+ else {
+ NETLOG_DBG("sendto_tm(%d): %zd bytes written", fd, r);
+ }
+ return r;
+}
+
* 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_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
+ *
*/
#ifndef NET_H_
*
* 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
- * non-blocking operation (immediate return).
+ * tcp_accept() to wait indefinitely. Set timeout to zero for immediate
+ * return.
*/
extern int tcp_accept(int sock, int timeout);
* 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
+ * 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
extern char *net_strerror(int errnum, char *buf, size_t len);
+/*
+ * 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.
+ */
+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.
+ */
+extern ssize_t sendto_tm(int fd, 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_))
+
+
#ifdef cplusplus
}
#endif
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <netinet/in.h>
#include "net.h"
#define LOG(...) fprintf(stderr, __VA_ARGS__)
-#define LOG_E(...) do{ \
- LOG("%s:%s@%d: ", __FILE__, __func__, __LINE__); \
- LOG(__VA_ARGS__); \
- LOG(": %s\n", strerror(errno)); \
- }while(0)
-
#define LOG_NET(e_,...) do{ \
char ebuf[100]; \
LOG("%s:%s@%d: ", __FILE__, __func__, __LINE__); \
LOG(": %s\n", net_strerror(e_,ebuf,sizeof ebuf)); \
}while(0)
+#define AFTOS(a_) ((a_)==AF_INET?"4":(a_)==AF_INET6?"6":"*")
+
int main(int argc, char *argv[]) {
int n, c, l;
+ int arg = 1, af = AF_UNSPEC, mode = 't';
+ char *nod = "localhost";
+ char *svc = "12345";
char buf[4000];
- signal(SIGPIPE, SIG_IGN);
-
if (argc < 2) {
- LOG("Usage: %s t|T|u|U\n", argv[0]);
+ LOG("Usage: %s [4|6] [t|T|u|U [address [service]]]\n", argv[0]);
exit(EXIT_FAILURE);
}
+ if (*argv[arg] == '4') {
+ af = AF_INET;
+ ++arg;
+ }
+ else if (*argv[arg] == '6') {
+ af = AF_INET6;
+ ++arg;
+ }
+ if (argv[arg])
+ mode = argv[arg++][0];
+ if (argv[arg])
+ nod = argv[arg++];
+ if (argv[arg])
+ svc = argv[arg++];
- switch (*argv[1]) {
+ switch (mode) {
default:
case 't':
- // TCP client
- // Remote: ncat -l localhost 12345
- c = tcp_open_client("localhost", "12345", "localhost");
+ // TCP generator client
+ printf("TCP%s client\n", AFTOS(af));
+ c = net_open_client(nod, svc, NULL, SOCK_STREAM, af);
if (c < 0) {
LOG_NET(c, "tcp_open_client");
exit(EXIT_FAILURE);
}
while (1) {
+ memset(buf, 0, sizeof buf);
sprintf(buf, "hello %ld\n", time(NULL));
- n = send(c, buf, strlen(buf), 0);
+ n = send_tm(c, buf, strlen(buf), 0, 5000);
if (n <= 0) {
- LOG_E("send");
+ LOG_NET(n, "send_tm");
+ break;
+ }
+ memset(buf, 0, sizeof buf);
+ n = recv_tm(c, buf, sizeof buf-1, 0, 5000);
+ if (n > 0) {
+ buf[n] = '\0';
+ printf("%s", buf);
+ }
+ else if (n < 0) {
+ LOG_NET(n, "recv");
break;
}
- if (getchar() == 'q')
+ else {
+ LOG("connection reset by peer\n");
break;
+ }
+ sleep(1);
}
c = net_close(c);
LOG_NET(c, "net_close");
break;
case 'T':
- // TCP server
- // Remote: ncat localhost 12345
- l = tcp6_open_server("", "12345");
+ // TCP echo server
+ printf("TCP%s server\n", AFTOS(af));
+ l = net_open_server(nod, svc, SOCK_STREAM, af);
if (l < 0) {
LOG_NET(l, "tcp6_open_server");
exit(EXIT_FAILURE);
if (c < 0) {
continue;
}
- memset(buf, 0, sizeof buf);
- n = recv(c, buf, sizeof buf-1, 0);
- if (n > 0) {
- printf("%s", buf);
- n = send(c, buf, n, 0);
- if (n <= 0) {
- LOG_E("send");
+ for (n = 1; n > 0; ) {
+ memset(buf, 0, sizeof buf);
+ n = recv_tm(c, buf, sizeof buf-1, 0, 5000);
+ if (n > 0) {
+ buf[n] = '\0';
+ printf("%s", buf);
+ n = send_tm(c, buf, n, 0, 5000);
+ if (n <= 0) {
+ LOG_NET(n, "send");
+ break;
+ }
+ }
+ else if (n < 0) {
+ LOG_NET(n, "recv");
+ break;
+ }
+ else {
+ LOG("connection reset by peer\n");
break;
}
- }
- else if (n < 0) {
- LOG_E("recv");
- break;
}
c = net_close(c);
LOG_NET(c, "net_close");
}
- c = net_close(c);
- LOG_NET(c, "net_close");
+ l = net_close(l);
+ LOG_NET(l, "net_close");
break;
case 'u':
- // UDP client
- // Remote: ncat -4 -u -l localhost 12345
- c = udp4_open_client(NULL, "12345", NULL);
+ // UDP generator client
+ printf("UDP%s client\n", AFTOS(af));
+ c = net_open_client(nod, svc, NULL, SOCK_DGRAM, af);
if (c < 0) {
LOG_NET(c, "udp4_open_client");
exit(EXIT_FAILURE);
}
while (1) {
sprintf(buf, "hello %ld\n", time(NULL));
- n = send(c, buf, strlen(buf), 0);
+ n = send_tm(c, buf, strlen(buf), 0, 5000);
if (n <= 0) {
- LOG_E("send");
+ LOG_NET(n, "send_tm");
+ break;
+ }
+ memset(buf, 0, sizeof buf);
+ n = recv_tm(c, buf, sizeof buf-1, 0, 5000);
+ if (n > 0) {
+ buf[n] = '\0';
+ printf("%s", buf);
+ }
+ else if (n < 0) {
+ LOG_NET(n, "recv");
+ break;
+ }
+ else {
+ LOG("connection reset by peer\n");
break;
}
sleep(1);
break;
case 'U':
- // UDP server
- // Remote: ncat -4 -u localhost 12345
- l = udp4_open_server(NULL, "12345");
- if (l < 0) {
- LOG_NET(l, "udp4_open_server");
+ // UDP echo server
+ printf("UDP%s server\n", AFTOS(af));
+ c = net_open_server(nod, svc, SOCK_DGRAM, af);
+ if (c < 0) {
+ LOG_NET(c, "udp4_open_server");
exit(EXIT_FAILURE);
}
while (1) {
- memset(buf, 0, sizeof buf);
- n = recv(l, buf, sizeof buf-1, 0);
- if (n > 0) {
- printf("%s", buf);
- }
- else {
- LOG_E("recv");
- break;
+ struct sockaddr_in6 addr;
+ struct sockaddr *paddr = (struct sockaddr *)&addr;
+ socklen_t addrlen = sizeof addr;
+ for (n = 1; n > 0; ) {
+ memset(buf, 0, sizeof buf);
+ n = recvfrom_tm(c, buf, sizeof buf-1, 0, paddr, &addrlen, 5000);
+ if (n > 0) {
+ buf[n] = '\0';
+ printf("%s", buf);
+ n = sendto_tm(c, buf, n, 0, paddr, addrlen, 5000);
+ if (n <= 0) {
+ LOG_NET(n, "send");
+ break;
+ }
+ }
+ else if (n < 0) {
+ LOG_NET(n, "recv");
+ break;
+ }
+ else {
+ LOG("connection reset by peer\n");
+ break;
+ }
}
}
- l = net_close(l);
- LOG_NET(l, "net_close");
+ c = net_close(c);
+ LOG_NET(c, "net_close");
break;
}