1/*-
2 * Copyright (c) 2018 Enji Cooper.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/mman.h>
32#include <sys/socket.h>
33#include <sys/stat.h>
34#include <sys/sysctl.h>
35#include <sys/uio.h>
36#include <err.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <netdb.h>
40#include <paths.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#include <atf-c.h>
47
48const char DETERMINISTIC_PATTERN[] =
49    "The past is already gone, the future is not yet here. There's only one moment for you to live.\n";
50
51#define	SOURCE_FILE		"source"
52#define	DESTINATION_FILE	"dest"
53
54#define	PORTRANGE_FIRST	"net.inet.ip.portrange.first"
55#define	PORTRANGE_LAST	"net.inet.ip.portrange.last"
56
57static int portrange_first, portrange_last;
58
59static int
60get_int_via_sysctlbyname(const char *oidname)
61{
62	size_t oldlen;
63	int int_value;
64
65	oldlen = sizeof(int_value);
66
67	ATF_REQUIRE_EQ_MSG(sysctlbyname(oidname, &int_value, &oldlen, NULL, 0),
68	    0, "sysctlbyname(%s, ...) failed: %s", oidname, strerror(errno));
69	ATF_REQUIRE_EQ_MSG(sizeof(int_value), oldlen, "sanity check failed");
70
71	return (int_value);
72}
73
74static int
75generate_random_port(int seed)
76{
77	int random_port;
78
79	printf("Generating a random port with seed=%d\n", seed);
80	if (portrange_first == 0) {
81		portrange_first = get_int_via_sysctlbyname(PORTRANGE_FIRST);
82		printf("Port range lower bound: %d\n", portrange_first);
83	}
84
85	if (portrange_last == 0) {
86		portrange_last = get_int_via_sysctlbyname(PORTRANGE_LAST);
87		printf("Port range upper bound: %d\n", portrange_last);
88	}
89
90	srand((unsigned)seed);
91
92	random_port = rand() % (portrange_last - portrange_first) +
93	    portrange_first;
94
95	printf("Random port generated: %d\n", random_port);
96	return (random_port);
97}
98
99static void
100resolve_localhost(struct addrinfo **res, int domain, int type, int port)
101{
102	const char *host;
103	char *serv;
104	struct addrinfo hints;
105	int error;
106
107	switch (domain) {
108	case AF_INET:
109		host = "127.0.0.1";
110		break;
111	case AF_INET6:
112		host = "::1";
113		break;
114	default:
115		atf_tc_fail("unhandled domain: %d", domain);
116	}
117
118	ATF_REQUIRE_MSG(asprintf(&serv, "%d", port) >= 0,
119	    "asprintf failed: %s", strerror(errno));
120
121	memset(&hints, 0, sizeof(hints));
122	hints.ai_family = domain;
123	hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV|AI_NUMERICHOST;
124	hints.ai_socktype = type;
125
126	error = getaddrinfo(host, serv, &hints, res);
127	ATF_REQUIRE_EQ_MSG(error, 0,
128	    "getaddrinfo failed: %s", gai_strerror(error));
129	free(serv);
130}
131
132static int
133make_socket(int domain, int type, int protocol)
134{
135	int sock;
136
137	sock = socket(domain, type, protocol);
138	ATF_REQUIRE_MSG(sock != -1, "socket(%d, %d, 0) failed: %s",
139	    domain, type, strerror(errno));
140
141	return (sock);
142}
143
144static int
145setup_client(int domain, int type, int port)
146{
147	struct addrinfo *res;
148	char host[NI_MAXHOST+1];
149	int error, sock;
150
151	resolve_localhost(&res, domain, type, port);
152	error = getnameinfo(
153	    (const struct sockaddr*)res->ai_addr, res->ai_addrlen,
154	    host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST);
155	ATF_REQUIRE_EQ_MSG(error, 0,
156	    "getnameinfo failed: %s", gai_strerror(error));
157	printf(
158	    "Will try to connect to host='%s', address_family=%d, "
159	    "socket_type=%d\n",
160	    host, res->ai_family, res->ai_socktype);
161	/* Avoid a double print when forked by flushing. */
162	fflush(stdout);
163	sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
164	error = connect(sock, (struct sockaddr*)res->ai_addr, res->ai_addrlen);
165	freeaddrinfo(res);
166	ATF_REQUIRE_EQ_MSG(error, 0, "connect failed: %s", strerror(errno));
167	return (sock);
168}
169
170/*
171 * XXX: use linear probing to find a free port and eliminate `port` argument as
172 * a [const] int (it will need to be a pointer so it can be passed back out of
173 * the function and can influence which port `setup_client(..)` connects on.
174 */
175static int
176setup_server(int domain, int type, int port)
177{
178	struct addrinfo *res;
179	char host[NI_MAXHOST+1];
180	int error, sock;
181
182	resolve_localhost(&res, domain, type, port);
183	sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
184
185	error = getnameinfo(
186	    (const struct sockaddr*)res->ai_addr, res->ai_addrlen,
187	    host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST);
188	ATF_REQUIRE_EQ_MSG(error, 0,
189	    "getnameinfo failed: %s", gai_strerror(error));
190	printf(
191	    "Will try to bind socket to host='%s', address_family=%d, "
192	    "socket_type=%d\n",
193	    host, res->ai_family, res->ai_socktype);
194	/* Avoid a double print when forked by flushing. */
195	fflush(stdout);
196	error = bind(sock, res->ai_addr, res->ai_addrlen);
197	freeaddrinfo(res);
198	ATF_REQUIRE_EQ_MSG(error, 0, "bind failed: %s", strerror(errno));
199	error = listen(sock, 1);
200	ATF_REQUIRE_EQ_MSG(error, 0, "listen failed: %s", strerror(errno));
201
202	return (sock);
203}
204
205/*
206 * This function is a helper routine for taking data being sent by `sendfile` via
207 * `server_sock`, and pushing the received stream out to a file, denoted by
208 * `dest_filename`.
209 */
210static void
211server_cat(const char *dest_filename, int server_sock, size_t len)
212{
213	char *buffer, *buf_window_ptr;
214	int recv_sock;
215	size_t buffer_size;
216	ssize_t received_bytes, recv_ret;
217
218	/*
219	 * Ensure that there isn't excess data sent across the wire by
220	 * capturing 10 extra bytes (plus 1 for nul).
221	 */
222	buffer_size = len + 10 + 1;
223	buffer = calloc(buffer_size, sizeof(char));
224	if (buffer == NULL)
225		err(1, "malloc failed");
226
227	recv_sock = accept(server_sock, NULL, 0);
228	if (recv_sock == -1)
229		err(1, "accept failed");
230
231	buf_window_ptr = buffer;
232	received_bytes = 0;
233	do {
234		recv_ret = recv(recv_sock, buf_window_ptr,
235		    buffer_size - received_bytes, 0);
236		if (recv_ret <= 0)
237			break;
238		buf_window_ptr += recv_ret;
239		received_bytes += recv_ret;
240	} while (received_bytes < buffer_size);
241
242	atf_utils_create_file(dest_filename, "%s", buffer);
243
244	(void)close(recv_sock);
245	(void)close(server_sock);
246	free(buffer);
247
248	if (received_bytes != len)
249		errx(1, "received unexpected data: %zd != %zd", received_bytes,
250		    len);
251}
252
253static int
254setup_tcp_server(int domain, int port)
255{
256
257	return (setup_server(domain, SOCK_STREAM, port));
258}
259
260static int
261setup_tcp_client(int domain, int port)
262{
263
264	return (setup_client(domain, SOCK_STREAM, port));
265}
266
267static off_t
268file_size_from_fd(int fd)
269{
270	struct stat st;
271
272	ATF_REQUIRE_EQ_MSG(0, fstat(fd, &st),
273	    "fstat failed: %s", strerror(errno));
274
275	return (st.st_size);
276}
277
278/*
279 * NB: `nbytes` == 0 has special connotations given the sendfile(2) API
280 * contract. In short, "send the whole file" (paraphrased).
281 */
282static void
283verify_source_and_dest(const char* dest_filename, int src_fd, off_t offset,
284    size_t nbytes)
285{
286	char *dest_pointer, *src_pointer;
287	off_t dest_file_size, src_file_size;
288	size_t length;
289	int dest_fd;
290
291	atf_utils_cat_file(dest_filename, "dest_file: ");
292
293	dest_fd = open(dest_filename, O_RDONLY);
294	ATF_REQUIRE_MSG(dest_fd != -1, "open failed");
295
296	dest_file_size = file_size_from_fd(dest_fd);
297	src_file_size = file_size_from_fd(src_fd);
298
299	/*
300	 * Per sendfile(2), "send the whole file" (paraphrased). This means
301	 * that we need to grab the file size, as passing in length = 0 with
302	 * mmap(2) will result in a failure with EINVAL (length = 0 is invalid).
303	 */
304	length = (nbytes == 0) ? (size_t)(src_file_size - offset) : nbytes;
305
306	ATF_REQUIRE_EQ_MSG(dest_file_size, length,
307	    "number of bytes written out to %s (%ju) doesn't match the "
308	    "expected number of bytes (%zu)", dest_filename, dest_file_size,
309	    length);
310
311	ATF_REQUIRE_EQ_MSG(0, lseek(src_fd, offset, SEEK_SET),
312	    "lseek failed: %s", strerror(errno));
313
314	dest_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, dest_fd, 0);
315	ATF_REQUIRE_MSG(dest_pointer != MAP_FAILED, "mmap failed: %s",
316	    strerror(errno));
317
318	printf("Will mmap in the source file from offset=%jd to length=%zu\n",
319	    offset, length);
320
321	src_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, src_fd, offset);
322	ATF_REQUIRE_MSG(src_pointer != MAP_FAILED, "mmap failed: %s",
323	    strerror(errno));
324
325	ATF_REQUIRE_EQ_MSG(0, memcmp(src_pointer, dest_pointer, length),
326	    "Contents of source and destination do not match. '%s' != '%s'",
327	    src_pointer, dest_pointer);
328
329	(void)munmap(src_pointer, length);
330	(void)munmap(dest_pointer, length);
331	(void)close(dest_fd);
332}
333
334static void
335fd_positive_file_test(int domain)
336{
337	off_t offset;
338	size_t nbytes, pattern_size;
339	int client_sock, error, fd, port, server_sock;
340	pid_t server_pid;
341
342	pattern_size = strlen(DETERMINISTIC_PATTERN);
343
344	atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
345	fd = open(SOURCE_FILE, O_RDONLY);
346	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
347
348	port = generate_random_port(__LINE__ + domain);
349	server_sock = setup_tcp_server(domain, port);
350	client_sock = setup_tcp_client(domain, port);
351
352	server_pid = atf_utils_fork();
353	if (server_pid == 0) {
354		(void)close(client_sock);
355		server_cat(DESTINATION_FILE, server_sock, pattern_size);
356		_exit(0);
357	} else
358		(void)close(server_sock);
359
360	nbytes = 0;
361	offset = 0;
362	error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
363	    SF_FLAGS(0, 0));
364	ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno));
365	(void)close(client_sock);
366
367	atf_utils_wait(server_pid, 0, "", "");
368	verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
369
370	(void)close(fd);
371}
372
373ATF_TC(fd_positive_file_v4);
374ATF_TC_HEAD(fd_positive_file_v4, tc)
375{
376
377	atf_tc_set_md_var(tc, "descr",
378	    "Verify regular file as file descriptor support (IPv4)");
379}
380ATF_TC_BODY(fd_positive_file_v4, tc)
381{
382
383	fd_positive_file_test(AF_INET);
384}
385
386ATF_TC(fd_positive_file_v6);
387ATF_TC_HEAD(fd_positive_file_v6, tc)
388{
389
390	atf_tc_set_md_var(tc, "descr",
391	    "Verify regular file as file descriptor support (IPv6)");
392}
393ATF_TC_BODY(fd_positive_file_v6, tc)
394{
395
396	fd_positive_file_test(AF_INET6);
397}
398
399static void
400fd_positive_shm_test(int domain)
401{
402	char *shm_pointer;
403	off_t offset;
404	size_t nbytes, pattern_size;
405	pid_t server_pid;
406	int client_sock, error, fd, port, server_sock;
407
408	pattern_size = strlen(DETERMINISTIC_PATTERN);
409
410	printf("pattern size: %zu\n", pattern_size);
411
412	fd = shm_open(SHM_ANON, O_RDWR|O_CREAT, 0600);
413	ATF_REQUIRE_MSG(fd != -1, "shm_open failed: %s", strerror(errno));
414	ATF_REQUIRE_EQ_MSG(0, ftruncate(fd, pattern_size),
415	    "ftruncate failed: %s", strerror(errno));
416	shm_pointer = mmap(NULL, pattern_size, PROT_READ|PROT_WRITE,
417	    MAP_SHARED, fd, 0);
418	ATF_REQUIRE_MSG(shm_pointer != MAP_FAILED,
419	    "mmap failed: %s", strerror(errno));
420	memcpy(shm_pointer, DETERMINISTIC_PATTERN, pattern_size);
421	ATF_REQUIRE_EQ_MSG(0,
422	    memcmp(shm_pointer, DETERMINISTIC_PATTERN, pattern_size),
423	    "memcmp showed data mismatch: '%s' != '%s'",
424	    DETERMINISTIC_PATTERN, shm_pointer);
425
426	port = generate_random_port(__LINE__ + domain);
427	server_sock = setup_tcp_server(domain, port);
428	client_sock = setup_tcp_client(domain, port);
429
430	server_pid = atf_utils_fork();
431	if (server_pid == 0) {
432		(void)close(client_sock);
433		server_cat(DESTINATION_FILE, server_sock, pattern_size);
434		_exit(0);
435	} else
436		(void)close(server_sock);
437
438	nbytes = 0;
439	offset = 0;
440	error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
441	    SF_FLAGS(0, 0));
442	ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno));
443	(void)close(client_sock);
444
445	atf_utils_wait(server_pid, 0, "", "");
446	verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
447
448	(void)munmap(shm_pointer, sizeof(DETERMINISTIC_PATTERN));
449	(void)close(fd);
450}
451
452ATF_TC(fd_positive_shm_v4);
453ATF_TC_HEAD(fd_positive_shm_v4, tc)
454{
455
456	atf_tc_set_md_var(tc, "descr",
457	    "Verify shared memory as file descriptor support (IPv4)");
458}
459ATF_TC_BODY(fd_positive_shm_v4, tc)
460{
461
462	fd_positive_shm_test(AF_INET);
463}
464
465ATF_TC(fd_positive_shm_v6);
466ATF_TC_HEAD(fd_positive_shm_v6, tc)
467{
468
469	atf_tc_set_md_var(tc, "descr",
470	    "Verify shared memory as file descriptor support (IPv6))");
471}
472ATF_TC_BODY(fd_positive_shm_v6, tc)
473{
474
475	fd_positive_shm_test(AF_INET6);
476}
477
478static void
479fd_negative_bad_fd_test(int domain)
480{
481	int client_sock, error, fd, port, server_sock;
482
483	port = generate_random_port(__LINE__ + domain);
484	server_sock = setup_tcp_server(domain, port);
485	client_sock = setup_tcp_client(domain, port);
486
487	fd = -1;
488
489	error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
490	ATF_REQUIRE_ERRNO(EBADF, error == -1);
491
492	(void)close(client_sock);
493	(void)close(server_sock);
494}
495
496ATF_TC(fd_negative_bad_fd_v4);
497ATF_TC_HEAD(fd_negative_bad_fd_v4, tc)
498{
499
500	atf_tc_set_md_var(tc, "descr",
501	    "Verify bad file descriptor returns EBADF (IPv4)");
502}
503ATF_TC_BODY(fd_negative_bad_fd_v4, tc)
504{
505
506	fd_negative_bad_fd_test(AF_INET);
507}
508
509ATF_TC(fd_negative_bad_fd_v6);
510ATF_TC_HEAD(fd_negative_bad_fd_v6, tc)
511{
512
513	atf_tc_set_md_var(tc, "descr",
514	    "Verify bad file descriptor returns EBADF (IPv6)");
515}
516ATF_TC_BODY(fd_negative_bad_fd_v6, tc)
517{
518
519	fd_negative_bad_fd_test(AF_INET6);
520}
521
522static void
523flags_test(int domain)
524{
525	off_t offset;
526	size_t nbytes, pattern_size;
527	int client_sock, error, fd, i, port, server_sock;
528	pid_t server_pid;
529	int16_t number_pages = 10;
530
531	pattern_size = strlen(DETERMINISTIC_PATTERN);
532
533	struct testcase {
534		int16_t readahead_pages, flags;
535	} testcases[] = {
536		/* This is covered in `:fd_positive_file` */
537#if 0
538		{
539			.readahead_pages = 0,
540			.flags = 0
541		},
542#endif
543		{
544			.readahead_pages = 0,
545			.flags = SF_NOCACHE
546		},
547#ifdef SF_USER_READAHEAD
548		{
549			.readahead_pages = 0,
550			.flags = SF_NOCACHE|SF_USER_READAHEAD
551		},
552		{
553			.readahead_pages = 0,
554			.flags = SF_USER_READAHEAD
555		},
556#endif
557		{
558			.readahead_pages = number_pages,
559			.flags = 0
560		},
561		{
562			.readahead_pages = number_pages,
563			.flags = SF_NOCACHE
564		},
565#ifdef SF_USER_READAHEAD
566		{
567			.readahead_pages = number_pages,
568			.flags = SF_NOCACHE|SF_USER_READAHEAD
569		},
570#endif
571		{
572			.readahead_pages = number_pages,
573			.flags = SF_NOCACHE
574		},
575		{
576			.readahead_pages = number_pages,
577			.flags = SF_NODISKIO
578		}
579	};
580
581	atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
582	for (i = 0; i < nitems(testcases); i++) {
583		fd = open(SOURCE_FILE, O_RDONLY);
584		ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
585
586		port = generate_random_port(i * __LINE__ + domain);
587		server_sock = setup_tcp_server(domain, port);
588		client_sock = setup_tcp_client(domain, port);
589
590		server_pid = atf_utils_fork();
591		if (server_pid == 0) {
592			(void)close(client_sock);
593			server_cat(DESTINATION_FILE, server_sock, pattern_size);
594			_exit(0);
595		} else
596			(void)close(server_sock);
597
598		nbytes = 0;
599		offset = 0;
600		error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
601		    SF_FLAGS(testcases[i].readahead_pages, testcases[i].flags));
602		ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s",
603		    i, strerror(errno));
604		(void)close(client_sock);
605
606		atf_utils_wait(server_pid, 0, "", "");
607		verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
608
609		(void)close(fd);
610	}
611}
612
613ATF_TC(flags_v4);
614ATF_TC_HEAD(flags_v4, tc)
615{
616
617	atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv4)");
618}
619ATF_TC_BODY(flags_v4, tc)
620{
621
622	flags_test(AF_INET);
623}
624
625ATF_TC(flags_v6);
626ATF_TC_HEAD(flags_v6, tc)
627{
628
629	atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv6)");
630}
631ATF_TC_BODY(flags_v6, tc)
632{
633
634	flags_test(AF_INET6);
635}
636
637static void
638hdtr_positive_test(int domain)
639{
640	struct iovec headers[1], trailers[1];
641	struct testcase {
642		bool include_headers, include_trailers;
643	} testcases[] = {
644		/* This is covered in `:fd_positive_file` */
645#if 0
646		{
647			.include_headers = false,
648			.include_trailers = false
649		},
650#endif
651		{
652			.include_headers = true,
653			.include_trailers = false
654		},
655		{
656			.include_headers = false,
657			.include_trailers = true
658		},
659		{
660			.include_headers = true,
661			.include_trailers = true
662		}
663	};
664	off_t offset;
665	size_t nbytes;
666	int client_sock, error, fd, fd2, i, port, rc, server_sock;
667	pid_t server_pid;
668
669	headers[0].iov_base = "This is a header";
670	headers[0].iov_len = strlen(headers[0].iov_base);
671	trailers[0].iov_base = "This is a trailer";
672	trailers[0].iov_len = strlen(trailers[0].iov_base);
673	offset = 0;
674	nbytes = 0;
675
676	for (i = 0; i < nitems(testcases); i++) {
677		struct sf_hdtr hdtr;
678		char *pattern;
679
680		if (testcases[i].include_headers) {
681			hdtr.headers = headers;
682			hdtr.hdr_cnt = nitems(headers);
683		} else {
684			hdtr.headers = NULL;
685			hdtr.hdr_cnt = 0;
686		}
687
688		if (testcases[i].include_trailers) {
689			hdtr.trailers = trailers;
690			hdtr.trl_cnt = nitems(trailers);
691		} else {
692			hdtr.trailers = NULL;
693			hdtr.trl_cnt = 0;
694		}
695
696		port = generate_random_port(i * __LINE__ + domain);
697		server_sock = setup_tcp_server(domain, port);
698		client_sock = setup_tcp_client(domain, port);
699
700		rc = asprintf(&pattern, "%s%s%s",
701		    testcases[i].include_headers ? (char *)headers[0].iov_base : "",
702		    DETERMINISTIC_PATTERN,
703		    testcases[i].include_trailers ? (char *)trailers[0].iov_base : "");
704		ATF_REQUIRE_MSG(rc != -1, "asprintf failed: %s", strerror(errno));
705
706		atf_utils_create_file(SOURCE_FILE ".full", "%s", pattern);
707		atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
708
709		fd = open(SOURCE_FILE, O_RDONLY);
710		ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
711
712		fd2 = open(SOURCE_FILE ".full", O_RDONLY);
713		ATF_REQUIRE_MSG(fd2 != -1, "open failed: %s", strerror(errno));
714
715		server_pid = atf_utils_fork();
716		if (server_pid == 0) {
717			(void)close(client_sock);
718			server_cat(DESTINATION_FILE, server_sock,
719			    strlen(pattern));
720			_exit(0);
721		} else
722			(void)close(server_sock);
723
724		error = sendfile(fd, client_sock, offset, nbytes, &hdtr,
725		    NULL, SF_FLAGS(0, 0));
726		ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s",
727		    i, strerror(errno));
728		(void)close(client_sock);
729
730		atf_utils_wait(server_pid, 0, "", "");
731		verify_source_and_dest(DESTINATION_FILE, fd2, offset, nbytes);
732
733		(void)close(fd);
734		(void)close(fd2);
735		free(pattern);
736		pattern = NULL;
737	}
738}
739
740ATF_TC(hdtr_positive_v4);
741ATF_TC_HEAD(hdtr_positive_v4, tc)
742{
743
744	atf_tc_set_md_var(tc, "descr",
745	    "Verify positive hdtr functionality (IPv4)");
746}
747ATF_TC_BODY(hdtr_positive_v4, tc)
748{
749
750	hdtr_positive_test(AF_INET);
751}
752
753ATF_TC(hdtr_positive_v6);
754ATF_TC_HEAD(hdtr_positive_v6, tc)
755{
756
757	atf_tc_set_md_var(tc, "descr",
758	    "Verify positive hdtr functionality (IPv6)");
759}
760ATF_TC_BODY(hdtr_positive_v6, tc)
761{
762
763	hdtr_positive_test(AF_INET);
764}
765
766static void
767hdtr_negative_bad_pointers_test(int domain)
768{
769	int client_sock, error, fd, port, server_sock;
770	struct sf_hdtr *hdtr1, hdtr2, hdtr3;
771
772	port = generate_random_port(__LINE__ + domain);
773
774	hdtr1 = (struct sf_hdtr*)-1;
775
776	memset(&hdtr2, 0, sizeof(hdtr2));
777	hdtr2.hdr_cnt = 1;
778	hdtr2.headers = (struct iovec*)-1;
779
780	memset(&hdtr3, 0, sizeof(hdtr3));
781	hdtr3.trl_cnt = 1;
782	hdtr3.trailers = (struct iovec*)-1;
783
784	fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
785	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
786
787	server_sock = setup_tcp_server(domain, port);
788	client_sock = setup_tcp_client(domain, port);
789
790	error = sendfile(fd, client_sock, 0, 0, hdtr1, NULL, SF_FLAGS(0, 0));
791	ATF_CHECK_ERRNO(EFAULT, error == -1);
792
793	error = sendfile(fd, client_sock, 0, 0, &hdtr2, NULL, SF_FLAGS(0, 0));
794	ATF_CHECK_ERRNO(EFAULT, error == -1);
795
796	error = sendfile(fd, client_sock, 0, 0, &hdtr3, NULL, SF_FLAGS(0, 0));
797	ATF_CHECK_ERRNO(EFAULT, error == -1);
798
799	(void)close(fd);
800	(void)close(client_sock);
801	(void)close(server_sock);
802}
803
804ATF_TC(hdtr_negative_bad_pointers_v4);
805ATF_TC_HEAD(hdtr_negative_bad_pointers_v4, tc)
806{
807
808	atf_tc_set_md_var(tc, "descr",
809	    "Verify that bad pointers for hdtr storage result in EFAULT (IPv4)");
810}
811ATF_TC_BODY(hdtr_negative_bad_pointers_v4, tc)
812{
813
814	hdtr_negative_bad_pointers_test(AF_INET);
815}
816
817ATF_TC(hdtr_negative_bad_pointers_v6);
818ATF_TC_HEAD(hdtr_negative_bad_pointers_v6, tc)
819{
820
821	atf_tc_set_md_var(tc, "descr",
822	    "Verify that bad pointers for hdtr storage result in EFAULT (IPv6)");
823}
824ATF_TC_BODY(hdtr_negative_bad_pointers_v6, tc)
825{
826
827	hdtr_negative_bad_pointers_test(AF_INET6);
828}
829
830static void
831offset_negative_value_less_than_zero_test(int domain)
832{
833	int client_sock, error, fd, port, server_sock;
834
835	port = generate_random_port(__LINE__ + domain);
836	server_sock = setup_tcp_server(domain, port);
837	client_sock = setup_tcp_client(domain, port);
838
839	fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
840	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
841
842	error = sendfile(fd, client_sock, -1, 0, NULL, NULL, SF_FLAGS(0, 0));
843	ATF_REQUIRE_ERRNO(EINVAL, error == -1);
844
845	(void)close(fd);
846	(void)close(client_sock);
847	(void)close(server_sock);
848}
849
850ATF_TC(offset_negative_value_less_than_zero_v4);
851ATF_TC_HEAD(offset_negative_value_less_than_zero_v4, tc)
852{
853
854	atf_tc_set_md_var(tc, "descr",
855	    "Verify that a negative offset results in EINVAL (IPv4)");
856}
857ATF_TC_BODY(offset_negative_value_less_than_zero_v4, tc)
858{
859
860	offset_negative_value_less_than_zero_test(AF_INET);
861}
862
863ATF_TC(offset_negative_value_less_than_zero_v6);
864ATF_TC_HEAD(offset_negative_value_less_than_zero_v6, tc)
865{
866
867	atf_tc_set_md_var(tc, "descr",
868	    "Verify that a negative offset results in EINVAL (IPv6)");
869}
870ATF_TC_BODY(offset_negative_value_less_than_zero_v6, tc)
871{
872
873	offset_negative_value_less_than_zero_test(AF_INET6);
874}
875
876static void
877sbytes_positive_test(int domain)
878{
879	size_t pattern_size = strlen(DETERMINISTIC_PATTERN);
880	off_t sbytes;
881	int client_sock, error, fd, port, server_sock;
882
883	port = generate_random_port(__LINE__ + domain);
884	server_sock = setup_tcp_server(domain, port);
885	client_sock = setup_tcp_client(domain, port);
886
887	atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
888	fd = open(SOURCE_FILE, O_RDONLY);
889	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
890
891	error = sendfile(fd, client_sock, 0, 0, NULL, &sbytes, SF_FLAGS(0, 0));
892	ATF_CHECK_EQ_MSG(error, 0, "sendfile failed: %s", strerror(errno));
893
894	(void)close(fd);
895	(void)close(client_sock);
896	(void)close(server_sock);
897
898	ATF_CHECK_EQ_MSG(pattern_size, sbytes,
899	    "the value returned by sbytes does not match the expected pattern "
900	    "size");
901}
902
903ATF_TC(sbytes_positive_v4);
904ATF_TC_HEAD(sbytes_positive_v4, tc)
905{
906
907	atf_tc_set_md_var(tc, "descr",
908	    "Verify positive `sbytes` functionality (IPv4)");
909}
910ATF_TC_BODY(sbytes_positive_v4, tc)
911{
912
913	sbytes_positive_test(AF_INET);
914}
915
916ATF_TC(sbytes_positive_v6);
917ATF_TC_HEAD(sbytes_positive_v6, tc)
918{
919
920	atf_tc_set_md_var(tc, "descr",
921	    "Verify positive `sbytes` functionality (IPv6)");
922}
923ATF_TC_BODY(sbytes_positive_v6, tc)
924{
925
926	sbytes_positive_test(AF_INET6);
927}
928
929static void
930sbytes_negative_test(int domain)
931{
932	off_t *sbytes_p = (off_t*)-1;
933	int client_sock, error, fd, port, server_sock;
934
935	port = generate_random_port(__LINE__ + domain);
936	server_sock = setup_tcp_server(domain, port);
937	client_sock = setup_tcp_client(domain, port);
938
939	atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
940	fd = open(SOURCE_FILE, O_RDONLY);
941	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
942
943	atf_tc_expect_fail(
944	    "bug 232210: EFAULT assert fails because copyout(9) call is not checked");
945
946	error = sendfile(fd, client_sock, 0, 0, NULL, sbytes_p, SF_FLAGS(0, 0));
947	ATF_REQUIRE_ERRNO(EFAULT, error == -1);
948
949	(void)close(fd);
950	(void)close(client_sock);
951	(void)close(server_sock);
952}
953
954ATF_TC(sbytes_negative_v4);
955ATF_TC_HEAD(sbytes_negative_v4, tc)
956{
957
958	atf_tc_set_md_var(tc, "descr",
959	    "Verify negative `sbytes` functionality (IPv4)");
960}
961ATF_TC_BODY(sbytes_negative_v4, tc)
962{
963
964	sbytes_negative_test(AF_INET);
965}
966
967ATF_TC(sbytes_negative_v6);
968ATF_TC_HEAD(sbytes_negative_v6, tc)
969{
970
971	atf_tc_set_md_var(tc, "descr",
972	    "Verify negative `sbytes` functionality (IPv6)");
973}
974ATF_TC_BODY(sbytes_negative_v6, tc)
975{
976
977	sbytes_negative_test(AF_INET6);
978}
979
980static void
981s_negative_not_connected_socket_test(int domain)
982{
983	int client_sock, error, fd, port;
984
985	port = generate_random_port(__LINE__ + domain);
986	client_sock = setup_tcp_server(domain, port);
987
988	fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
989	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
990
991	error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
992	ATF_REQUIRE_ERRNO(ENOTCONN, error == -1);
993
994	(void)close(fd);
995	(void)close(client_sock);
996}
997
998ATF_TC(s_negative_not_connected_socket_v4);
999ATF_TC_HEAD(s_negative_not_connected_socket_v4, tc)
1000{
1001
1002	atf_tc_set_md_var(tc, "descr",
1003	    "Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv4)");
1004}
1005
1006ATF_TC_BODY(s_negative_not_connected_socket_v4, tc)
1007{
1008
1009	s_negative_not_connected_socket_test(AF_INET);
1010}
1011
1012ATF_TC(s_negative_not_connected_socket_v6);
1013ATF_TC_HEAD(s_negative_not_connected_socket_v6, tc)
1014{
1015
1016	atf_tc_set_md_var(tc, "descr",
1017	    "Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv6)");
1018}
1019
1020ATF_TC_BODY(s_negative_not_connected_socket_v6, tc)
1021{
1022
1023	s_negative_not_connected_socket_test(AF_INET6);
1024}
1025
1026ATF_TC(s_negative_not_descriptor);
1027ATF_TC_HEAD(s_negative_not_descriptor, tc)
1028{
1029
1030	atf_tc_set_md_var(tc, "descr",
1031	    "Verify that an invalid file descriptor, e.g., -1, fails with EBADF");
1032}
1033
1034ATF_TC_BODY(s_negative_not_descriptor, tc)
1035{
1036	int client_sock, error, fd;
1037
1038	client_sock = -1;
1039
1040	fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
1041	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1042
1043	error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
1044	ATF_REQUIRE_ERRNO(EBADF, error == -1);
1045
1046	(void)close(fd);
1047}
1048
1049ATF_TC(s_negative_not_socket_file_descriptor);
1050ATF_TC_HEAD(s_negative_not_socket_file_descriptor, tc)
1051{
1052
1053	atf_tc_set_md_var(tc, "descr",
1054	    "Verify that a non-socket file descriptor fails with ENOTSOCK");
1055}
1056
1057ATF_TC_BODY(s_negative_not_socket_file_descriptor, tc)
1058{
1059	int client_sock, error, fd;
1060
1061	fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
1062	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1063
1064	client_sock = open(_PATH_DEVNULL, O_WRONLY);
1065	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1066
1067	error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
1068	ATF_REQUIRE_ERRNO(ENOTSOCK, error == -1);
1069
1070	(void)close(fd);
1071	(void)close(client_sock);
1072}
1073
1074static void
1075s_negative_udp_socket_test(int domain)
1076{
1077	int client_sock, error, fd, port;
1078
1079	port = generate_random_port(__LINE__ + domain);
1080	client_sock = setup_client(domain, SOCK_DGRAM, port);
1081
1082	fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
1083	ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1084
1085	error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
1086	ATF_REQUIRE_ERRNO(EINVAL, error == -1);
1087
1088	(void)close(fd);
1089	(void)close(client_sock);
1090}
1091
1092ATF_TC(s_negative_udp_socket_v4);
1093ATF_TC_HEAD(s_negative_udp_socket_v4, tc)
1094{
1095
1096	atf_tc_set_md_var(tc, "descr",
1097	    "Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv4)");
1098}
1099ATF_TC_BODY(s_negative_udp_socket_v4, tc)
1100{
1101
1102	s_negative_udp_socket_test(AF_INET);
1103}
1104
1105ATF_TC(s_negative_udp_socket_v6);
1106ATF_TC_HEAD(s_negative_udp_socket_v6, tc)
1107{
1108
1109	atf_tc_set_md_var(tc, "descr",
1110	    "Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv6)");
1111}
1112ATF_TC_BODY(s_negative_udp_socket_v6, tc)
1113{
1114
1115	s_negative_udp_socket_test(AF_INET6);
1116}
1117
1118ATF_TP_ADD_TCS(tp)
1119{
1120
1121	ATF_TP_ADD_TC(tp, fd_positive_file_v4);
1122	ATF_TP_ADD_TC(tp, fd_positive_file_v6);
1123	ATF_TP_ADD_TC(tp, fd_positive_shm_v4);
1124	ATF_TP_ADD_TC(tp, fd_positive_shm_v6);
1125	ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v4);
1126	ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v6);
1127	ATF_TP_ADD_TC(tp, flags_v4);
1128	ATF_TP_ADD_TC(tp, flags_v6);
1129	/*
1130	 * TODO: the negative case for SF_NODISKIO (returns EBUSY if file in
1131	 * use) is not covered yet.
1132	 *
1133	 * Need to lock a file in a subprocess in write mode, then try and
1134	 * send the data in read mode with sendfile.
1135	 *
1136	 * This should work with FFS/UFS, but there are no guarantees about
1137	 * other filesystem implementations of sendfile(2), e.g., ZFS.
1138	 */
1139	ATF_TP_ADD_TC(tp, hdtr_positive_v4);
1140	ATF_TP_ADD_TC(tp, hdtr_positive_v6);
1141	ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v4);
1142	ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v6);
1143	ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v4);
1144	ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v6);
1145	ATF_TP_ADD_TC(tp, sbytes_positive_v4);
1146	ATF_TP_ADD_TC(tp, sbytes_positive_v6);
1147	ATF_TP_ADD_TC(tp, sbytes_negative_v4);
1148	ATF_TP_ADD_TC(tp, sbytes_negative_v6);
1149	ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v4);
1150	ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v6);
1151	ATF_TP_ADD_TC(tp, s_negative_not_descriptor);
1152	ATF_TP_ADD_TC(tp, s_negative_not_socket_file_descriptor);
1153	ATF_TP_ADD_TC(tp, s_negative_udp_socket_v4);
1154	ATF_TP_ADD_TC(tp, s_negative_udp_socket_v6);
1155
1156	return (atf_no_error());
1157}
1158