18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#define _GNU_SOURCE
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <errno.h>
68c2ecf20Sopenharmony_ci#include <limits.h>
78c2ecf20Sopenharmony_ci#include <fcntl.h>
88c2ecf20Sopenharmony_ci#include <string.h>
98c2ecf20Sopenharmony_ci#include <stdbool.h>
108c2ecf20Sopenharmony_ci#include <stdint.h>
118c2ecf20Sopenharmony_ci#include <stdio.h>
128c2ecf20Sopenharmony_ci#include <stdlib.h>
138c2ecf20Sopenharmony_ci#include <strings.h>
148c2ecf20Sopenharmony_ci#include <signal.h>
158c2ecf20Sopenharmony_ci#include <unistd.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <sys/poll.h>
188c2ecf20Sopenharmony_ci#include <sys/random.h>
198c2ecf20Sopenharmony_ci#include <sys/sendfile.h>
208c2ecf20Sopenharmony_ci#include <sys/stat.h>
218c2ecf20Sopenharmony_ci#include <sys/socket.h>
228c2ecf20Sopenharmony_ci#include <sys/types.h>
238c2ecf20Sopenharmony_ci#include <sys/mman.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <netdb.h>
268c2ecf20Sopenharmony_ci#include <netinet/in.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/tcp.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciextern int optind;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#ifndef IPPROTO_MPTCP
338c2ecf20Sopenharmony_ci#define IPPROTO_MPTCP 262
348c2ecf20Sopenharmony_ci#endif
358c2ecf20Sopenharmony_ci#ifndef TCP_ULP
368c2ecf20Sopenharmony_ci#define TCP_ULP 31
378c2ecf20Sopenharmony_ci#endif
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int  poll_timeout = 10 * 1000;
408c2ecf20Sopenharmony_cistatic bool listen_mode;
418c2ecf20Sopenharmony_cistatic bool quit;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cienum cfg_mode {
448c2ecf20Sopenharmony_ci	CFG_MODE_POLL,
458c2ecf20Sopenharmony_ci	CFG_MODE_MMAP,
468c2ecf20Sopenharmony_ci	CFG_MODE_SENDFILE,
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic enum cfg_mode cfg_mode = CFG_MODE_POLL;
508c2ecf20Sopenharmony_cistatic const char *cfg_host;
518c2ecf20Sopenharmony_cistatic const char *cfg_port	= "12000";
528c2ecf20Sopenharmony_cistatic int cfg_sock_proto	= IPPROTO_MPTCP;
538c2ecf20Sopenharmony_cistatic bool tcpulp_audit;
548c2ecf20Sopenharmony_cistatic int pf = AF_INET;
558c2ecf20Sopenharmony_cistatic int cfg_sndbuf;
568c2ecf20Sopenharmony_cistatic int cfg_rcvbuf;
578c2ecf20Sopenharmony_cistatic bool cfg_join;
588c2ecf20Sopenharmony_cistatic bool cfg_remove;
598c2ecf20Sopenharmony_cistatic int cfg_wait;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic void die_usage(void)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] [-m mode]"
648c2ecf20Sopenharmony_ci		"[-l] [-w sec] connect_address\n");
658c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-6 use ipv6\n");
668c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-t num -- set poll timeout to num\n");
678c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n");
688c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n");
698c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-p num -- use port num\n");
708c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n");
718c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n");
728c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-u -- check mptcp ulp\n");
738c2ecf20Sopenharmony_ci	fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n");
748c2ecf20Sopenharmony_ci	exit(1);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic void handle_signal(int nr)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	quit = true;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic const char *getxinfo_strerr(int err)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	if (err == EAI_SYSTEM)
858c2ecf20Sopenharmony_ci		return strerror(errno);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return gai_strerror(err);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void xgetnameinfo(const struct sockaddr *addr, socklen_t addrlen,
918c2ecf20Sopenharmony_ci			 char *host, socklen_t hostlen,
928c2ecf20Sopenharmony_ci			 char *serv, socklen_t servlen)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	int flags = NI_NUMERICHOST | NI_NUMERICSERV;
958c2ecf20Sopenharmony_ci	int err = getnameinfo(addr, addrlen, host, hostlen, serv, servlen,
968c2ecf20Sopenharmony_ci			      flags);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (err) {
998c2ecf20Sopenharmony_ci		const char *errstr = getxinfo_strerr(err);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		fprintf(stderr, "Fatal: getnameinfo: %s\n", errstr);
1028c2ecf20Sopenharmony_ci		exit(1);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void xgetaddrinfo(const char *node, const char *service,
1078c2ecf20Sopenharmony_ci			 const struct addrinfo *hints,
1088c2ecf20Sopenharmony_ci			 struct addrinfo **res)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	int err = getaddrinfo(node, service, hints, res);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (err) {
1138c2ecf20Sopenharmony_ci		const char *errstr = getxinfo_strerr(err);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
1168c2ecf20Sopenharmony_ci			node ? node : "", service ? service : "", errstr);
1178c2ecf20Sopenharmony_ci		exit(1);
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void set_rcvbuf(int fd, unsigned int size)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int err;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
1268c2ecf20Sopenharmony_ci	if (err) {
1278c2ecf20Sopenharmony_ci		perror("set SO_RCVBUF");
1288c2ecf20Sopenharmony_ci		exit(1);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void set_sndbuf(int fd, unsigned int size)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	int err;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
1378c2ecf20Sopenharmony_ci	if (err) {
1388c2ecf20Sopenharmony_ci		perror("set SO_SNDBUF");
1398c2ecf20Sopenharmony_ci		exit(1);
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic int sock_listen_mptcp(const char * const listenaddr,
1448c2ecf20Sopenharmony_ci			     const char * const port)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	int sock;
1478c2ecf20Sopenharmony_ci	struct addrinfo hints = {
1488c2ecf20Sopenharmony_ci		.ai_protocol = IPPROTO_TCP,
1498c2ecf20Sopenharmony_ci		.ai_socktype = SOCK_STREAM,
1508c2ecf20Sopenharmony_ci		.ai_flags = AI_PASSIVE | AI_NUMERICHOST
1518c2ecf20Sopenharmony_ci	};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	hints.ai_family = pf;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	struct addrinfo *a, *addr;
1568c2ecf20Sopenharmony_ci	int one = 1;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	xgetaddrinfo(listenaddr, port, &hints, &addr);
1598c2ecf20Sopenharmony_ci	hints.ai_family = pf;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	for (a = addr; a; a = a->ai_next) {
1628c2ecf20Sopenharmony_ci		sock = socket(a->ai_family, a->ai_socktype, cfg_sock_proto);
1638c2ecf20Sopenharmony_ci		if (sock < 0)
1648c2ecf20Sopenharmony_ci			continue;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci		if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
1678c2ecf20Sopenharmony_ci				     sizeof(one)))
1688c2ecf20Sopenharmony_ci			perror("setsockopt");
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
1718c2ecf20Sopenharmony_ci			break; /* success */
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		perror("bind");
1748c2ecf20Sopenharmony_ci		close(sock);
1758c2ecf20Sopenharmony_ci		sock = -1;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	freeaddrinfo(addr);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (sock < 0) {
1818c2ecf20Sopenharmony_ci		fprintf(stderr, "Could not create listen socket\n");
1828c2ecf20Sopenharmony_ci		return sock;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (listen(sock, 20)) {
1868c2ecf20Sopenharmony_ci		perror("listen");
1878c2ecf20Sopenharmony_ci		close(sock);
1888c2ecf20Sopenharmony_ci		return -1;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return sock;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic bool sock_test_tcpulp(const char * const remoteaddr,
1958c2ecf20Sopenharmony_ci			     const char * const port)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct addrinfo hints = {
1988c2ecf20Sopenharmony_ci		.ai_protocol = IPPROTO_TCP,
1998c2ecf20Sopenharmony_ci		.ai_socktype = SOCK_STREAM,
2008c2ecf20Sopenharmony_ci	};
2018c2ecf20Sopenharmony_ci	struct addrinfo *a, *addr;
2028c2ecf20Sopenharmony_ci	int sock = -1, ret = 0;
2038c2ecf20Sopenharmony_ci	bool test_pass = false;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	hints.ai_family = AF_INET;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	xgetaddrinfo(remoteaddr, port, &hints, &addr);
2088c2ecf20Sopenharmony_ci	for (a = addr; a; a = a->ai_next) {
2098c2ecf20Sopenharmony_ci		sock = socket(a->ai_family, a->ai_socktype, IPPROTO_TCP);
2108c2ecf20Sopenharmony_ci		if (sock < 0) {
2118c2ecf20Sopenharmony_ci			perror("socket");
2128c2ecf20Sopenharmony_ci			continue;
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci		ret = setsockopt(sock, IPPROTO_TCP, TCP_ULP, "mptcp",
2158c2ecf20Sopenharmony_ci				 sizeof("mptcp"));
2168c2ecf20Sopenharmony_ci		if (ret == -1 && errno == EOPNOTSUPP)
2178c2ecf20Sopenharmony_ci			test_pass = true;
2188c2ecf20Sopenharmony_ci		close(sock);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		if (test_pass)
2218c2ecf20Sopenharmony_ci			break;
2228c2ecf20Sopenharmony_ci		if (!ret)
2238c2ecf20Sopenharmony_ci			fprintf(stderr,
2248c2ecf20Sopenharmony_ci				"setsockopt(TCP_ULP) returned 0\n");
2258c2ecf20Sopenharmony_ci		else
2268c2ecf20Sopenharmony_ci			perror("setsockopt(TCP_ULP)");
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci	return test_pass;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int sock_connect_mptcp(const char * const remoteaddr,
2328c2ecf20Sopenharmony_ci			      const char * const port, int proto)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct addrinfo hints = {
2358c2ecf20Sopenharmony_ci		.ai_protocol = IPPROTO_TCP,
2368c2ecf20Sopenharmony_ci		.ai_socktype = SOCK_STREAM,
2378c2ecf20Sopenharmony_ci	};
2388c2ecf20Sopenharmony_ci	struct addrinfo *a, *addr;
2398c2ecf20Sopenharmony_ci	int sock = -1;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	hints.ai_family = pf;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	xgetaddrinfo(remoteaddr, port, &hints, &addr);
2448c2ecf20Sopenharmony_ci	for (a = addr; a; a = a->ai_next) {
2458c2ecf20Sopenharmony_ci		sock = socket(a->ai_family, a->ai_socktype, proto);
2468c2ecf20Sopenharmony_ci		if (sock < 0) {
2478c2ecf20Sopenharmony_ci			perror("socket");
2488c2ecf20Sopenharmony_ci			continue;
2498c2ecf20Sopenharmony_ci		}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
2528c2ecf20Sopenharmony_ci			break; /* success */
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		perror("connect()");
2558c2ecf20Sopenharmony_ci		close(sock);
2568c2ecf20Sopenharmony_ci		sock = -1;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	freeaddrinfo(addr);
2608c2ecf20Sopenharmony_ci	return sock;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic size_t do_rnd_write(const int fd, char *buf, const size_t len)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	static bool first = true;
2668c2ecf20Sopenharmony_ci	unsigned int do_w;
2678c2ecf20Sopenharmony_ci	ssize_t bw;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	do_w = rand() & 0xffff;
2708c2ecf20Sopenharmony_ci	if (do_w == 0 || do_w > len)
2718c2ecf20Sopenharmony_ci		do_w = len;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (cfg_join && first && do_w > 100)
2748c2ecf20Sopenharmony_ci		do_w = 100;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (cfg_remove && do_w > 50)
2778c2ecf20Sopenharmony_ci		do_w = 50;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	bw = write(fd, buf, do_w);
2808c2ecf20Sopenharmony_ci	if (bw < 0)
2818c2ecf20Sopenharmony_ci		perror("write");
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* let the join handshake complete, before going on */
2848c2ecf20Sopenharmony_ci	if (cfg_join && first) {
2858c2ecf20Sopenharmony_ci		usleep(200000);
2868c2ecf20Sopenharmony_ci		first = false;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (cfg_remove)
2908c2ecf20Sopenharmony_ci		usleep(200000);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return bw;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic size_t do_write(const int fd, char *buf, const size_t len)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	size_t offset = 0;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	while (offset < len) {
3008c2ecf20Sopenharmony_ci		size_t written;
3018c2ecf20Sopenharmony_ci		ssize_t bw;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		bw = write(fd, buf + offset, len - offset);
3048c2ecf20Sopenharmony_ci		if (bw < 0) {
3058c2ecf20Sopenharmony_ci			perror("write");
3068c2ecf20Sopenharmony_ci			return 0;
3078c2ecf20Sopenharmony_ci		}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		written = (size_t)bw;
3108c2ecf20Sopenharmony_ci		offset += written;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return offset;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	size_t cap = rand();
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	cap &= 0xffff;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (cap == 0)
3238c2ecf20Sopenharmony_ci		cap = 1;
3248c2ecf20Sopenharmony_ci	else if (cap > len)
3258c2ecf20Sopenharmony_ci		cap = len;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return read(fd, buf, cap);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void set_nonblock(int fd)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	int flags = fcntl(fd, F_GETFL);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (flags == -1)
3358c2ecf20Sopenharmony_ci		return;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int copyfd_io_poll(int infd, int peerfd, int outfd)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct pollfd fds = {
3438c2ecf20Sopenharmony_ci		.fd = peerfd,
3448c2ecf20Sopenharmony_ci		.events = POLLIN | POLLOUT,
3458c2ecf20Sopenharmony_ci	};
3468c2ecf20Sopenharmony_ci	unsigned int woff = 0, wlen = 0;
3478c2ecf20Sopenharmony_ci	char wbuf[8192];
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	set_nonblock(peerfd);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	for (;;) {
3528c2ecf20Sopenharmony_ci		char rbuf[8192];
3538c2ecf20Sopenharmony_ci		ssize_t len;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		if (fds.events == 0)
3568c2ecf20Sopenharmony_ci			break;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		switch (poll(&fds, 1, poll_timeout)) {
3598c2ecf20Sopenharmony_ci		case -1:
3608c2ecf20Sopenharmony_ci			if (errno == EINTR)
3618c2ecf20Sopenharmony_ci				continue;
3628c2ecf20Sopenharmony_ci			perror("poll");
3638c2ecf20Sopenharmony_ci			return 1;
3648c2ecf20Sopenharmony_ci		case 0:
3658c2ecf20Sopenharmony_ci			fprintf(stderr, "%s: poll timed out (events: "
3668c2ecf20Sopenharmony_ci				"POLLIN %u, POLLOUT %u)\n", __func__,
3678c2ecf20Sopenharmony_ci				fds.events & POLLIN, fds.events & POLLOUT);
3688c2ecf20Sopenharmony_ci			return 2;
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		if (fds.revents & POLLIN) {
3728c2ecf20Sopenharmony_ci			len = do_rnd_read(peerfd, rbuf, sizeof(rbuf));
3738c2ecf20Sopenharmony_ci			if (len == 0) {
3748c2ecf20Sopenharmony_ci				/* no more data to receive:
3758c2ecf20Sopenharmony_ci				 * peer has closed its write side
3768c2ecf20Sopenharmony_ci				 */
3778c2ecf20Sopenharmony_ci				fds.events &= ~POLLIN;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci				if ((fds.events & POLLOUT) == 0)
3808c2ecf20Sopenharmony_ci					/* and nothing more to send */
3818c2ecf20Sopenharmony_ci					break;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci			/* Else, still have data to transmit */
3848c2ecf20Sopenharmony_ci			} else if (len < 0) {
3858c2ecf20Sopenharmony_ci				perror("read");
3868c2ecf20Sopenharmony_ci				return 3;
3878c2ecf20Sopenharmony_ci			}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci			do_write(outfd, rbuf, len);
3908c2ecf20Sopenharmony_ci		}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		if (fds.revents & POLLOUT) {
3938c2ecf20Sopenharmony_ci			if (wlen == 0) {
3948c2ecf20Sopenharmony_ci				woff = 0;
3958c2ecf20Sopenharmony_ci				wlen = read(infd, wbuf, sizeof(wbuf));
3968c2ecf20Sopenharmony_ci			}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci			if (wlen > 0) {
3998c2ecf20Sopenharmony_ci				ssize_t bw;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci				bw = do_rnd_write(peerfd, wbuf + woff, wlen);
4028c2ecf20Sopenharmony_ci				if (bw < 0)
4038c2ecf20Sopenharmony_ci					return 111;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci				woff += bw;
4068c2ecf20Sopenharmony_ci				wlen -= bw;
4078c2ecf20Sopenharmony_ci			} else if (wlen == 0) {
4088c2ecf20Sopenharmony_ci				/* We have no more data to send. */
4098c2ecf20Sopenharmony_ci				fds.events &= ~POLLOUT;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci				if ((fds.events & POLLIN) == 0)
4128c2ecf20Sopenharmony_ci					/* ... and peer also closed already */
4138c2ecf20Sopenharmony_ci					break;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci				/* ... but we still receive.
4168c2ecf20Sopenharmony_ci				 * Close our write side, ev. give some time
4178c2ecf20Sopenharmony_ci				 * for address notification and/or checking
4188c2ecf20Sopenharmony_ci				 * the current status
4198c2ecf20Sopenharmony_ci				 */
4208c2ecf20Sopenharmony_ci				if (cfg_wait)
4218c2ecf20Sopenharmony_ci					usleep(cfg_wait);
4228c2ecf20Sopenharmony_ci				shutdown(peerfd, SHUT_WR);
4238c2ecf20Sopenharmony_ci			} else {
4248c2ecf20Sopenharmony_ci				if (errno == EINTR)
4258c2ecf20Sopenharmony_ci					continue;
4268c2ecf20Sopenharmony_ci				perror("read");
4278c2ecf20Sopenharmony_ci				return 4;
4288c2ecf20Sopenharmony_ci			}
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		if (fds.revents & (POLLERR | POLLNVAL)) {
4328c2ecf20Sopenharmony_ci			fprintf(stderr, "Unexpected revents: "
4338c2ecf20Sopenharmony_ci				"POLLERR/POLLNVAL(%x)\n", fds.revents);
4348c2ecf20Sopenharmony_ci			return 5;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* leave some time for late join/announce */
4398c2ecf20Sopenharmony_ci	if (cfg_join || cfg_remove)
4408c2ecf20Sopenharmony_ci		usleep(cfg_wait);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	close(peerfd);
4438c2ecf20Sopenharmony_ci	return 0;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic int do_recvfile(int infd, int outfd)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	ssize_t r;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	do {
4518c2ecf20Sopenharmony_ci		char buf[16384];
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		r = do_rnd_read(infd, buf, sizeof(buf));
4548c2ecf20Sopenharmony_ci		if (r > 0) {
4558c2ecf20Sopenharmony_ci			if (write(outfd, buf, r) != r)
4568c2ecf20Sopenharmony_ci				break;
4578c2ecf20Sopenharmony_ci		} else if (r < 0) {
4588c2ecf20Sopenharmony_ci			perror("read");
4598c2ecf20Sopenharmony_ci		}
4608c2ecf20Sopenharmony_ci	} while (r > 0);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return (int)r;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int do_mmap(int infd, int outfd, unsigned int size)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	char *inbuf = mmap(NULL, size, PROT_READ, MAP_SHARED, infd, 0);
4688c2ecf20Sopenharmony_ci	ssize_t ret = 0, off = 0;
4698c2ecf20Sopenharmony_ci	size_t rem;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (inbuf == MAP_FAILED) {
4728c2ecf20Sopenharmony_ci		perror("mmap");
4738c2ecf20Sopenharmony_ci		return 1;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	rem = size;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	while (rem > 0) {
4798c2ecf20Sopenharmony_ci		ret = write(outfd, inbuf + off, rem);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		if (ret < 0) {
4828c2ecf20Sopenharmony_ci			perror("write");
4838c2ecf20Sopenharmony_ci			break;
4848c2ecf20Sopenharmony_ci		}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		off += ret;
4878c2ecf20Sopenharmony_ci		rem -= ret;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	munmap(inbuf, size);
4918c2ecf20Sopenharmony_ci	return rem;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic int get_infd_size(int fd)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	struct stat sb;
4978c2ecf20Sopenharmony_ci	ssize_t count;
4988c2ecf20Sopenharmony_ci	int err;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	err = fstat(fd, &sb);
5018c2ecf20Sopenharmony_ci	if (err < 0) {
5028c2ecf20Sopenharmony_ci		perror("fstat");
5038c2ecf20Sopenharmony_ci		return -1;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if ((sb.st_mode & S_IFMT) != S_IFREG) {
5078c2ecf20Sopenharmony_ci		fprintf(stderr, "%s: stdin is not a regular file\n", __func__);
5088c2ecf20Sopenharmony_ci		return -2;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	count = sb.st_size;
5128c2ecf20Sopenharmony_ci	if (count > INT_MAX) {
5138c2ecf20Sopenharmony_ci		fprintf(stderr, "File too large: %zu\n", count);
5148c2ecf20Sopenharmony_ci		return -3;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return (int)count;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int do_sendfile(int infd, int outfd, unsigned int count)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	while (count > 0) {
5238c2ecf20Sopenharmony_ci		ssize_t r;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		r = sendfile(outfd, infd, NULL, count);
5268c2ecf20Sopenharmony_ci		if (r < 0) {
5278c2ecf20Sopenharmony_ci			perror("sendfile");
5288c2ecf20Sopenharmony_ci			return 3;
5298c2ecf20Sopenharmony_ci		}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci		count -= r;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	return 0;
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic int copyfd_io_mmap(int infd, int peerfd, int outfd,
5388c2ecf20Sopenharmony_ci			  unsigned int size)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	int err;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (listen_mode) {
5438c2ecf20Sopenharmony_ci		err = do_recvfile(peerfd, outfd);
5448c2ecf20Sopenharmony_ci		if (err)
5458c2ecf20Sopenharmony_ci			return err;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci		err = do_mmap(infd, peerfd, size);
5488c2ecf20Sopenharmony_ci	} else {
5498c2ecf20Sopenharmony_ci		err = do_mmap(infd, peerfd, size);
5508c2ecf20Sopenharmony_ci		if (err)
5518c2ecf20Sopenharmony_ci			return err;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		shutdown(peerfd, SHUT_WR);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		err = do_recvfile(peerfd, outfd);
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	return err;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int copyfd_io_sendfile(int infd, int peerfd, int outfd,
5628c2ecf20Sopenharmony_ci			      unsigned int size)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	int err;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (listen_mode) {
5678c2ecf20Sopenharmony_ci		err = do_recvfile(peerfd, outfd);
5688c2ecf20Sopenharmony_ci		if (err)
5698c2ecf20Sopenharmony_ci			return err;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci		err = do_sendfile(infd, peerfd, size);
5728c2ecf20Sopenharmony_ci	} else {
5738c2ecf20Sopenharmony_ci		err = do_sendfile(infd, peerfd, size);
5748c2ecf20Sopenharmony_ci		if (err)
5758c2ecf20Sopenharmony_ci			return err;
5768c2ecf20Sopenharmony_ci		err = do_recvfile(peerfd, outfd);
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	return err;
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic int copyfd_io(int infd, int peerfd, int outfd)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	int file_size;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	switch (cfg_mode) {
5878c2ecf20Sopenharmony_ci	case CFG_MODE_POLL:
5888c2ecf20Sopenharmony_ci		return copyfd_io_poll(infd, peerfd, outfd);
5898c2ecf20Sopenharmony_ci	case CFG_MODE_MMAP:
5908c2ecf20Sopenharmony_ci		file_size = get_infd_size(infd);
5918c2ecf20Sopenharmony_ci		if (file_size < 0)
5928c2ecf20Sopenharmony_ci			return file_size;
5938c2ecf20Sopenharmony_ci		return copyfd_io_mmap(infd, peerfd, outfd, file_size);
5948c2ecf20Sopenharmony_ci	case CFG_MODE_SENDFILE:
5958c2ecf20Sopenharmony_ci		file_size = get_infd_size(infd);
5968c2ecf20Sopenharmony_ci		if (file_size < 0)
5978c2ecf20Sopenharmony_ci			return file_size;
5988c2ecf20Sopenharmony_ci		return copyfd_io_sendfile(infd, peerfd, outfd, file_size);
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	fprintf(stderr, "Invalid mode %d\n", cfg_mode);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	die_usage();
6048c2ecf20Sopenharmony_ci	return 1;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic void check_sockaddr(int pf, struct sockaddr_storage *ss,
6088c2ecf20Sopenharmony_ci			   socklen_t salen)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct sockaddr_in6 *sin6;
6118c2ecf20Sopenharmony_ci	struct sockaddr_in *sin;
6128c2ecf20Sopenharmony_ci	socklen_t wanted_size = 0;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	switch (pf) {
6158c2ecf20Sopenharmony_ci	case AF_INET:
6168c2ecf20Sopenharmony_ci		wanted_size = sizeof(*sin);
6178c2ecf20Sopenharmony_ci		sin = (void *)ss;
6188c2ecf20Sopenharmony_ci		if (!sin->sin_port)
6198c2ecf20Sopenharmony_ci			fprintf(stderr, "accept: something wrong: ip connection from port 0");
6208c2ecf20Sopenharmony_ci		break;
6218c2ecf20Sopenharmony_ci	case AF_INET6:
6228c2ecf20Sopenharmony_ci		wanted_size = sizeof(*sin6);
6238c2ecf20Sopenharmony_ci		sin6 = (void *)ss;
6248c2ecf20Sopenharmony_ci		if (!sin6->sin6_port)
6258c2ecf20Sopenharmony_ci			fprintf(stderr, "accept: something wrong: ipv6 connection from port 0");
6268c2ecf20Sopenharmony_ci		break;
6278c2ecf20Sopenharmony_ci	default:
6288c2ecf20Sopenharmony_ci		fprintf(stderr, "accept: Unknown pf %d, salen %u\n", pf, salen);
6298c2ecf20Sopenharmony_ci		return;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (salen != wanted_size)
6338c2ecf20Sopenharmony_ci		fprintf(stderr, "accept: size mismatch, got %d expected %d\n",
6348c2ecf20Sopenharmony_ci			(int)salen, wanted_size);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (ss->ss_family != pf)
6378c2ecf20Sopenharmony_ci		fprintf(stderr, "accept: pf mismatch, expect %d, ss_family is %d\n",
6388c2ecf20Sopenharmony_ci			(int)ss->ss_family, pf);
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic void check_getpeername(int fd, struct sockaddr_storage *ss, socklen_t salen)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	struct sockaddr_storage peerss;
6448c2ecf20Sopenharmony_ci	socklen_t peersalen = sizeof(peerss);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (getpeername(fd, (struct sockaddr *)&peerss, &peersalen) < 0) {
6478c2ecf20Sopenharmony_ci		perror("getpeername");
6488c2ecf20Sopenharmony_ci		return;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (peersalen != salen) {
6528c2ecf20Sopenharmony_ci		fprintf(stderr, "%s: %d vs %d\n", __func__, peersalen, salen);
6538c2ecf20Sopenharmony_ci		return;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (memcmp(ss, &peerss, peersalen)) {
6578c2ecf20Sopenharmony_ci		char a[INET6_ADDRSTRLEN];
6588c2ecf20Sopenharmony_ci		char b[INET6_ADDRSTRLEN];
6598c2ecf20Sopenharmony_ci		char c[INET6_ADDRSTRLEN];
6608c2ecf20Sopenharmony_ci		char d[INET6_ADDRSTRLEN];
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci		xgetnameinfo((struct sockaddr *)ss, salen,
6638c2ecf20Sopenharmony_ci			     a, sizeof(a), b, sizeof(b));
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci		xgetnameinfo((struct sockaddr *)&peerss, peersalen,
6668c2ecf20Sopenharmony_ci			     c, sizeof(c), d, sizeof(d));
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci		fprintf(stderr, "%s: memcmp failure: accept %s vs peername %s, %s vs %s salen %d vs %d\n",
6698c2ecf20Sopenharmony_ci			__func__, a, c, b, d, peersalen, salen);
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cistatic void check_getpeername_connect(int fd)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	struct sockaddr_storage ss;
6768c2ecf20Sopenharmony_ci	socklen_t salen = sizeof(ss);
6778c2ecf20Sopenharmony_ci	char a[INET6_ADDRSTRLEN];
6788c2ecf20Sopenharmony_ci	char b[INET6_ADDRSTRLEN];
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (getpeername(fd, (struct sockaddr *)&ss, &salen) < 0) {
6818c2ecf20Sopenharmony_ci		perror("getpeername");
6828c2ecf20Sopenharmony_ci		return;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	xgetnameinfo((struct sockaddr *)&ss, salen,
6868c2ecf20Sopenharmony_ci		     a, sizeof(a), b, sizeof(b));
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if (strcmp(cfg_host, a) || strcmp(cfg_port, b))
6898c2ecf20Sopenharmony_ci		fprintf(stderr, "%s: %s vs %s, %s vs %s\n", __func__,
6908c2ecf20Sopenharmony_ci			cfg_host, a, cfg_port, b);
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic void maybe_close(int fd)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	unsigned int r = rand();
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (!(cfg_join || cfg_remove) && (r & 1))
6988c2ecf20Sopenharmony_ci		close(fd);
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ciint main_loop_s(int listensock)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct sockaddr_storage ss;
7048c2ecf20Sopenharmony_ci	struct pollfd polls;
7058c2ecf20Sopenharmony_ci	socklen_t salen;
7068c2ecf20Sopenharmony_ci	int remotesock;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	polls.fd = listensock;
7098c2ecf20Sopenharmony_ci	polls.events = POLLIN;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	switch (poll(&polls, 1, poll_timeout)) {
7128c2ecf20Sopenharmony_ci	case -1:
7138c2ecf20Sopenharmony_ci		perror("poll");
7148c2ecf20Sopenharmony_ci		return 1;
7158c2ecf20Sopenharmony_ci	case 0:
7168c2ecf20Sopenharmony_ci		fprintf(stderr, "%s: timed out\n", __func__);
7178c2ecf20Sopenharmony_ci		close(listensock);
7188c2ecf20Sopenharmony_ci		return 2;
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	salen = sizeof(ss);
7228c2ecf20Sopenharmony_ci	remotesock = accept(listensock, (struct sockaddr *)&ss, &salen);
7238c2ecf20Sopenharmony_ci	if (remotesock >= 0) {
7248c2ecf20Sopenharmony_ci		maybe_close(listensock);
7258c2ecf20Sopenharmony_ci		check_sockaddr(pf, &ss, salen);
7268c2ecf20Sopenharmony_ci		check_getpeername(remotesock, &ss, salen);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci		return copyfd_io(0, remotesock, 1);
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	perror("accept");
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return 1;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic void init_rng(void)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	unsigned int foo;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (getrandom(&foo, sizeof(foo), 0) == -1) {
7418c2ecf20Sopenharmony_ci		perror("getrandom");
7428c2ecf20Sopenharmony_ci		exit(1);
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	srand(foo);
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ciint main_loop(void)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	int fd;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	/* listener is ready. */
7538c2ecf20Sopenharmony_ci	fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto);
7548c2ecf20Sopenharmony_ci	if (fd < 0)
7558c2ecf20Sopenharmony_ci		return 2;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	check_getpeername_connect(fd);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (cfg_rcvbuf)
7608c2ecf20Sopenharmony_ci		set_rcvbuf(fd, cfg_rcvbuf);
7618c2ecf20Sopenharmony_ci	if (cfg_sndbuf)
7628c2ecf20Sopenharmony_ci		set_sndbuf(fd, cfg_sndbuf);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	return copyfd_io(0, fd, 1);
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ciint parse_proto(const char *proto)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	if (!strcasecmp(proto, "MPTCP"))
7708c2ecf20Sopenharmony_ci		return IPPROTO_MPTCP;
7718c2ecf20Sopenharmony_ci	if (!strcasecmp(proto, "TCP"))
7728c2ecf20Sopenharmony_ci		return IPPROTO_TCP;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	fprintf(stderr, "Unknown protocol: %s\n.", proto);
7758c2ecf20Sopenharmony_ci	die_usage();
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	/* silence compiler warning */
7788c2ecf20Sopenharmony_ci	return 0;
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ciint parse_mode(const char *mode)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	if (!strcasecmp(mode, "poll"))
7848c2ecf20Sopenharmony_ci		return CFG_MODE_POLL;
7858c2ecf20Sopenharmony_ci	if (!strcasecmp(mode, "mmap"))
7868c2ecf20Sopenharmony_ci		return CFG_MODE_MMAP;
7878c2ecf20Sopenharmony_ci	if (!strcasecmp(mode, "sendfile"))
7888c2ecf20Sopenharmony_ci		return CFG_MODE_SENDFILE;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	fprintf(stderr, "Unknown test mode: %s\n", mode);
7918c2ecf20Sopenharmony_ci	fprintf(stderr, "Supported modes are:\n");
7928c2ecf20Sopenharmony_ci	fprintf(stderr, "\t\t\"poll\" - interleaved read/write using poll()\n");
7938c2ecf20Sopenharmony_ci	fprintf(stderr, "\t\t\"mmap\" - send entire input file (mmap+write), then read response (-l will read input first)\n");
7948c2ecf20Sopenharmony_ci	fprintf(stderr, "\t\t\"sendfile\" - send entire input file (sendfile), then read response (-l will read input first)\n");
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	die_usage();
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	/* silence compiler warning */
7998c2ecf20Sopenharmony_ci	return 0;
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int parse_int(const char *size)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	unsigned long s;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	errno = 0;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	s = strtoul(size, NULL, 0);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (errno) {
8118c2ecf20Sopenharmony_ci		fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
8128c2ecf20Sopenharmony_ci			size, strerror(errno));
8138c2ecf20Sopenharmony_ci		die_usage();
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (s > INT_MAX) {
8178c2ecf20Sopenharmony_ci		fprintf(stderr, "Invalid sndbuf size %s (%s)\n",
8188c2ecf20Sopenharmony_ci			size, strerror(ERANGE));
8198c2ecf20Sopenharmony_ci		die_usage();
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	return (int)s;
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic void parse_opts(int argc, char **argv)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	int c;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	while ((c = getopt(argc, argv, "6jrlp:s:hut:m:S:R:w:")) != -1) {
8308c2ecf20Sopenharmony_ci		switch (c) {
8318c2ecf20Sopenharmony_ci		case 'j':
8328c2ecf20Sopenharmony_ci			cfg_join = true;
8338c2ecf20Sopenharmony_ci			cfg_mode = CFG_MODE_POLL;
8348c2ecf20Sopenharmony_ci			cfg_wait = 400000;
8358c2ecf20Sopenharmony_ci			break;
8368c2ecf20Sopenharmony_ci		case 'r':
8378c2ecf20Sopenharmony_ci			cfg_remove = true;
8388c2ecf20Sopenharmony_ci			cfg_mode = CFG_MODE_POLL;
8398c2ecf20Sopenharmony_ci			cfg_wait = 400000;
8408c2ecf20Sopenharmony_ci			break;
8418c2ecf20Sopenharmony_ci		case 'l':
8428c2ecf20Sopenharmony_ci			listen_mode = true;
8438c2ecf20Sopenharmony_ci			break;
8448c2ecf20Sopenharmony_ci		case 'p':
8458c2ecf20Sopenharmony_ci			cfg_port = optarg;
8468c2ecf20Sopenharmony_ci			break;
8478c2ecf20Sopenharmony_ci		case 's':
8488c2ecf20Sopenharmony_ci			cfg_sock_proto = parse_proto(optarg);
8498c2ecf20Sopenharmony_ci			break;
8508c2ecf20Sopenharmony_ci		case 'h':
8518c2ecf20Sopenharmony_ci			die_usage();
8528c2ecf20Sopenharmony_ci			break;
8538c2ecf20Sopenharmony_ci		case 'u':
8548c2ecf20Sopenharmony_ci			tcpulp_audit = true;
8558c2ecf20Sopenharmony_ci			break;
8568c2ecf20Sopenharmony_ci		case '6':
8578c2ecf20Sopenharmony_ci			pf = AF_INET6;
8588c2ecf20Sopenharmony_ci			break;
8598c2ecf20Sopenharmony_ci		case 't':
8608c2ecf20Sopenharmony_ci			poll_timeout = atoi(optarg) * 1000;
8618c2ecf20Sopenharmony_ci			if (poll_timeout <= 0)
8628c2ecf20Sopenharmony_ci				poll_timeout = -1;
8638c2ecf20Sopenharmony_ci			break;
8648c2ecf20Sopenharmony_ci		case 'm':
8658c2ecf20Sopenharmony_ci			cfg_mode = parse_mode(optarg);
8668c2ecf20Sopenharmony_ci			break;
8678c2ecf20Sopenharmony_ci		case 'S':
8688c2ecf20Sopenharmony_ci			cfg_sndbuf = parse_int(optarg);
8698c2ecf20Sopenharmony_ci			break;
8708c2ecf20Sopenharmony_ci		case 'R':
8718c2ecf20Sopenharmony_ci			cfg_rcvbuf = parse_int(optarg);
8728c2ecf20Sopenharmony_ci			break;
8738c2ecf20Sopenharmony_ci		case 'w':
8748c2ecf20Sopenharmony_ci			cfg_wait = atoi(optarg)*1000000;
8758c2ecf20Sopenharmony_ci			break;
8768c2ecf20Sopenharmony_ci		}
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (optind + 1 != argc)
8808c2ecf20Sopenharmony_ci		die_usage();
8818c2ecf20Sopenharmony_ci	cfg_host = argv[optind];
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	if (strchr(cfg_host, ':'))
8848c2ecf20Sopenharmony_ci		pf = AF_INET6;
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ciint main(int argc, char *argv[])
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	init_rng();
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	signal(SIGUSR1, handle_signal);
8928c2ecf20Sopenharmony_ci	parse_opts(argc, argv);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	if (tcpulp_audit)
8958c2ecf20Sopenharmony_ci		return sock_test_tcpulp(cfg_host, cfg_port) ? 0 : 1;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	if (listen_mode) {
8988c2ecf20Sopenharmony_ci		int fd = sock_listen_mptcp(cfg_host, cfg_port);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci		if (fd < 0)
9018c2ecf20Sopenharmony_ci			return 1;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci		if (cfg_rcvbuf)
9048c2ecf20Sopenharmony_ci			set_rcvbuf(fd, cfg_rcvbuf);
9058c2ecf20Sopenharmony_ci		if (cfg_sndbuf)
9068c2ecf20Sopenharmony_ci			set_sndbuf(fd, cfg_sndbuf);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		return main_loop_s(fd);
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return main_loop();
9128c2ecf20Sopenharmony_ci}
913