#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct err_msg { int size; int len; char msg[0]; }; const int zero = 0; const int one = 1; const struct linger nolinger = { .l_onoff = 1, .l_linger = 0 }; #define TRASH_SIZE 65536 static char trash[TRASH_SIZE]; volatile int nbproc = 0; static struct timeval start_time; static int showtime; static int verbose; static int pid; /* display the message and exit with the code */ __attribute__((noreturn)) void die(int code, const char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); exit(code); } /* display the usage message and exit with the code */ __attribute__((noreturn)) void usage(int code, const char *arg0) { die(code, "Usage: %s [:]port [action*]\n", arg0); } void dolog(const char *format, ...) { struct timeval date, tv; int delay; va_list args; if (!verbose) return; if (showtime) { gettimeofday(&date, NULL); switch (showtime) { case 1: // [msec] relative delay = (date.tv_sec - start_time.tv_sec) * 1000000 + date.tv_usec - start_time.tv_usec; fprintf(stderr, "[%d] ", delay / 1000); break; case 2: // [sec.usec] relative tv.tv_usec = date.tv_usec - start_time.tv_usec; tv.tv_sec = date.tv_sec - start_time.tv_sec; if ((signed)tv.tv_sec > 0) { if ((signed)tv.tv_usec < 0) { tv.tv_usec += 1000000; tv.tv_sec--; } } else if (tv.tv_sec == 0) { if ((signed)tv.tv_usec < 0) tv.tv_usec = 0; } else { tv.tv_sec = 0; tv.tv_usec = 0; } fprintf(stderr, "[%d.%06d] ", tv.tv_sec, tv.tv_usec); break; default: // [sec.usec] absolute fprintf(stderr, "[%d.%06d] ", date.tv_sec, date.tv_usec); break; } } fprintf(stderr, "%5d ", pid); va_start(args, format); vfprintf(stderr, format, args); va_end(args); } struct err_msg *alloc_err_msg(int size) { struct err_msg *err; err = malloc(sizeof(*err) + size); if (err) { err->len = 0; err->size = size; } return err; } void sig_handler(int sig) { if (sig == SIGCHLD) { while (waitpid(-1, NULL, WNOHANG) > 0) __sync_sub_and_fetch(&nbproc, 1); } } /* converts str in the form [[||]:]port to struct sockaddr_storage. * Returns < 0 with err set in case of error. */ int addr_to_ss(char *str, struct sockaddr_storage *ss, struct err_msg *err) { char *port_str; int port; memset(ss, 0, sizeof(*ss)); /* look for the addr/port delimiter, it's the last colon. If there's no * colon, it's 0:. */ if ((port_str = strrchr(str, ':')) == NULL) { port = atoi(str); if (port <= 0 || port > 65535) { err->len = snprintf(err->msg, err->size, "Missing/invalid port number: '%s'\n", str); return -1; } ss->ss_family = AF_INET; ((struct sockaddr_in *)ss)->sin_port = htons(port); ((struct sockaddr_in *)ss)->sin_addr.s_addr = INADDR_ANY; return 0; } *port_str++ = 0; if (strrchr(str, ':') != NULL) { /* IPv6 address contains ':' */ ss->ss_family = AF_INET6; ((struct sockaddr_in6 *)ss)->sin6_port = htons(atoi(port_str)); if (!inet_pton(ss->ss_family, str, &((struct sockaddr_in6 *)ss)->sin6_addr)) { err->len = snprintf(err->msg, err->size, "Invalid server address: '%s'\n", str); return -1; } } else { ss->ss_family = AF_INET; ((struct sockaddr_in *)ss)->sin_port = htons(atoi(port_str)); if (*str == '*' || *str == '\0') { /* INADDR_ANY */ ((struct sockaddr_in *)ss)->sin_addr.s_addr = INADDR_ANY; return 0; } if (!inet_pton(ss->ss_family, str, &((struct sockaddr_in *)ss)->sin_addr)) { struct hostent *he = gethostbyname(str); if (he == NULL) { err->len = snprintf(err->msg, err->size, "Invalid server name: '%s'\n", str); return -1; } ((struct sockaddr_in *)ss)->sin_addr = *(struct in_addr *) *(he->h_addr_list); } } return 0; } /* waits up to one second on fd for events (POLLIN|POLLOUT). * returns poll's status. */ int wait_on_fd(int fd, int events) { struct pollfd pollfd; int ret; do { pollfd.fd = fd; pollfd.events = events; ret = poll(&pollfd, 1, 1000); } while (ret == -1 && errno == EINTR); return ret; } int tcp_set_nodelay(int sock, const char *arg) { return setsockopt(sock, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); } int tcp_set_nolinger(int sock, const char *arg) { return setsockopt(sock, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); } int tcp_set_noquickack(int sock, const char *arg) { /* warning: do not use during connect if nothing is to be sent! */ return setsockopt(sock, SOL_TCP, TCP_QUICKACK, &zero, sizeof(zero)); } /* Try to listen to address . Return the fd or -1 in case of error */ int tcp_listen(const struct sockaddr_storage *sa, const char *arg) { int sock; int backlog; if (arg[1]) backlog = atoi(arg + 1); else backlog = 1000; if (backlog < 0 || backlog > 65535) { fprintf(stderr, "backlog must be between 0 and 65535 inclusive (was %d)\n", backlog); return -1; } sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) { perror("socket()"); return -1; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) { perror("setsockopt(SO_REUSEADDR)"); goto fail; } #ifdef SO_REUSEPORT if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *) &one, sizeof(one)) == -1) { perror("setsockopt(SO_REUSEPORT)"); goto fail; } #endif if (bind(sock, (struct sockaddr *)sa, sa->ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == -1) { perror("bind"); goto fail; } if (listen(sock, backlog) == -1) { perror("listen"); goto fail; } return sock; fail: close(sock); return -1; } /* accepts a socket from listening socket , and returns it (or -1 in case of error) */ int tcp_accept(int sock, const char *arg) { int count; int newsock; if (arg[1]) count = atoi(arg + 1); else count = 1; if (count <= 0) { fprintf(stderr, "accept count must be > 0 or unset (was %d)\n", count); return -1; } do { newsock = accept(sock, NULL, NULL); if (newsock < 0) { // TODO: improve error handling if (errno == EINTR || errno == EAGAIN || errno == ECONNABORTED) continue; perror("accept()"); break; } if (count > 1) close(newsock); count--; } while (count > 0); fcntl(newsock, F_SETFL, O_NONBLOCK); return newsock; } /* Try to establish a new connection to . Return the fd or -1 in case of error */ int tcp_connect(const struct sockaddr_storage *sa, const char *arg) { int sock; sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) return -1; if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) goto fail; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) goto fail; if (connect(sock, (const struct sockaddr *)sa, sizeof(*sa)) < 0) { if (errno != EINPROGRESS) goto fail; } return sock; fail: close(sock); return -1; } /* receives N bytes from the socket and returns 0 (or -1 in case of error). * When no arg is passed, receives anything and stops. Otherwise reads the * requested amount of data. 0 means read as much as possible. */ int tcp_recv(int sock, const char *arg) { int count = -1; // stop at first read int ret; if (arg[1]) { count = atoi(arg + 1); if (count < 0) { fprintf(stderr, "recv count must be >= 0 or unset (was %d)\n", count); return -1; } } while (1) { ret = recv(sock, NULL, (count > 0) ? count : INT_MAX, MSG_NOSIGNAL | MSG_TRUNC); if (ret < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { dolog("recv %d\n", ret); return -1; } while (!wait_on_fd(sock, POLLIN)); continue; } dolog("recv %d\n", ret); if (!ret) break; if (!count) continue; else if (count > 0) count -= ret; if (count <= 0) break; } return 0; } /* sends N bytes to the socket and returns 0 (or -1 in case of error). If not * set, sends only one block. Sending zero means try to send forever. */ int tcp_send(int sock, const char *arg) { int count = -1; // stop after first block int ret; if (arg[1]) { count = atoi(arg + 1); if (count < 0) { fprintf(stderr, "send count must be >= 0 or unset (was %d)\n", count); return -1; } } while (1) { ret = send(sock, trash, (count > 0) && (count < sizeof(trash)) ? count : sizeof(trash), MSG_NOSIGNAL | ((count > sizeof(trash)) ? MSG_MORE : 0)); if (ret < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { dolog("send %d\n", ret); return -1; } while (!wait_on_fd(sock, POLLOUT)); continue; } dolog("send %d\n", ret); if (!count) continue; else if (count > 0) count -= ret; if (count <= 0) break; } return 0; } /* echoes N bytes to the socket and returns 0 (or -1 in case of error). If not * set, echoes only the first block. Zero means forward forever. */ int tcp_echo(int sock, const char *arg) { int count = -1; // echo forever int ret; int rcvd; if (arg[1]) { count = atoi(arg + 1); if (count < 0) { fprintf(stderr, "send count must be >= 0 or unset (was %d)\n", count); return -1; } } rcvd = 0; while (1) { if (rcvd <= 0) { /* no data pending */ rcvd = recv(sock, trash, (count > 0) && (count < sizeof(trash)) ? count : sizeof(trash), MSG_NOSIGNAL); if (rcvd < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { dolog("recv %d\n", rcvd); return -1; } while (!wait_on_fd(sock, POLLIN)); continue; } dolog("recv %d\n", rcvd); if (!rcvd) break; } else { /* some data still pending */ ret = send(sock, trash, rcvd, MSG_NOSIGNAL | ((count > rcvd) ? MSG_MORE : 0)); if (ret < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { dolog("send %d\n", ret); return -1; } while (!wait_on_fd(sock, POLLOUT)); continue; } dolog("send %d\n", ret); rcvd -= ret; if (rcvd) continue; if (!count) continue; else if (count > 0) count -= ret; if (count <= 0) break; } } return 0; } /* waits for an event on the socket, usually indicates an accept for a * listening socket and a connect for an outgoing socket. */ int tcp_wait(int sock, const char *arg) { struct pollfd pollfd; int delay = -1; // wait forever int ret; if (arg[1]) { delay = atoi(arg + 1); if (delay < 0) { fprintf(stderr, "wait time must be >= 0 or unset (was %d)\n", delay); return -1; } } /* FIXME: this doesn't take into account delivered signals */ do { pollfd.fd = sock; pollfd.events = POLLIN | POLLOUT; ret = poll(&pollfd, 1, delay); } while (ret == -1 && errno == EINTR); if (ret > 0 && pollfd.revents & POLLERR) return -1; return 0; } /* waits for the input data to be present */ int tcp_wait_in(int sock, const char *arg) { struct pollfd pollfd; int ret; do { pollfd.fd = sock; pollfd.events = POLLIN; ret = poll(&pollfd, 1, 1000); } while (ret == -1 && errno == EINTR); if (ret > 0 && pollfd.revents & POLLERR) return -1; return 0; } /* waits for the output queue to be empty */ int tcp_wait_out(int sock, const char *arg) { struct pollfd pollfd; int ret; do { pollfd.fd = sock; pollfd.events = POLLOUT; ret = poll(&pollfd, 1, 1000); } while (ret == -1 && errno == EINTR); if (ret > 0 && pollfd.revents & POLLERR) return -1; /* Now wait for data to leave the socket */ do { if (ioctl(sock, TIOCOUTQ, &ret) < 0) return -1; } while (ret > 0); return 0; } /* delays processing for