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