xref: /freebsd/lib/libc/tests/sys/sendfile_test.c (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
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 	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 
74 static int
75 generate_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 
99 static void
100 resolve_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 
132 static int
133 make_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 
144 static int
145 setup_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  */
175 static int
176 setup_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  */
210 static void
211 server_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 
253 static int
254 setup_tcp_server(int domain, int port)
255 {
256 
257 	return (setup_server(domain, SOCK_STREAM, port));
258 }
259 
260 static int
261 setup_tcp_client(int domain, int port)
262 {
263 
264 	return (setup_client(domain, SOCK_STREAM, port));
265 }
266 
267 static off_t
268 file_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  */
282 static void
283 verify_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 
334 static void
335 fd_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 
373 ATF_TC(fd_positive_file_v4);
374 ATF_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 }
380 ATF_TC_BODY(fd_positive_file_v4, tc)
381 {
382 
383 	fd_positive_file_test(AF_INET);
384 }
385 
386 ATF_TC(fd_positive_file_v6);
387 ATF_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 }
393 ATF_TC_BODY(fd_positive_file_v6, tc)
394 {
395 
396 	fd_positive_file_test(AF_INET6);
397 }
398 
399 static void
400 fd_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 
452 ATF_TC(fd_positive_shm_v4);
453 ATF_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 }
459 ATF_TC_BODY(fd_positive_shm_v4, tc)
460 {
461 
462 	fd_positive_shm_test(AF_INET);
463 }
464 
465 ATF_TC(fd_positive_shm_v6);
466 ATF_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 }
472 ATF_TC_BODY(fd_positive_shm_v6, tc)
473 {
474 
475 	fd_positive_shm_test(AF_INET6);
476 }
477 
478 static void
479 fd_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 
496 ATF_TC(fd_negative_bad_fd_v4);
497 ATF_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 }
503 ATF_TC_BODY(fd_negative_bad_fd_v4, tc)
504 {
505 
506 	fd_negative_bad_fd_test(AF_INET);
507 }
508 
509 ATF_TC(fd_negative_bad_fd_v6);
510 ATF_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 }
516 ATF_TC_BODY(fd_negative_bad_fd_v6, tc)
517 {
518 
519 	fd_negative_bad_fd_test(AF_INET6);
520 }
521 
522 static void
523 flags_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 
613 ATF_TC(flags_v4);
614 ATF_TC_HEAD(flags_v4, tc)
615 {
616 
617 	atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv4)");
618 }
619 ATF_TC_BODY(flags_v4, tc)
620 {
621 
622 	flags_test(AF_INET);
623 }
624 
625 ATF_TC(flags_v6);
626 ATF_TC_HEAD(flags_v6, tc)
627 {
628 
629 	atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv6)");
630 }
631 ATF_TC_BODY(flags_v6, tc)
632 {
633 
634 	flags_test(AF_INET6);
635 }
636 
637 static void
638 hdtr_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 
740 ATF_TC(hdtr_positive_v4);
741 ATF_TC_HEAD(hdtr_positive_v4, tc)
742 {
743 
744 	atf_tc_set_md_var(tc, "descr",
745 	    "Verify positive hdtr functionality (IPv4)");
746 }
747 ATF_TC_BODY(hdtr_positive_v4, tc)
748 {
749 
750 	hdtr_positive_test(AF_INET);
751 }
752 
753 ATF_TC(hdtr_positive_v6);
754 ATF_TC_HEAD(hdtr_positive_v6, tc)
755 {
756 
757 	atf_tc_set_md_var(tc, "descr",
758 	    "Verify positive hdtr functionality (IPv6)");
759 }
760 ATF_TC_BODY(hdtr_positive_v6, tc)
761 {
762 
763 	hdtr_positive_test(AF_INET);
764 }
765 
766 static void
767 hdtr_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 
804 ATF_TC(hdtr_negative_bad_pointers_v4);
805 ATF_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 }
811 ATF_TC_BODY(hdtr_negative_bad_pointers_v4, tc)
812 {
813 
814 	hdtr_negative_bad_pointers_test(AF_INET);
815 }
816 
817 ATF_TC(hdtr_negative_bad_pointers_v6);
818 ATF_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 }
824 ATF_TC_BODY(hdtr_negative_bad_pointers_v6, tc)
825 {
826 
827 	hdtr_negative_bad_pointers_test(AF_INET6);
828 }
829 
830 static void
831 offset_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 
850 ATF_TC(offset_negative_value_less_than_zero_v4);
851 ATF_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 }
857 ATF_TC_BODY(offset_negative_value_less_than_zero_v4, tc)
858 {
859 
860 	offset_negative_value_less_than_zero_test(AF_INET);
861 }
862 
863 ATF_TC(offset_negative_value_less_than_zero_v6);
864 ATF_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 }
870 ATF_TC_BODY(offset_negative_value_less_than_zero_v6, tc)
871 {
872 
873 	offset_negative_value_less_than_zero_test(AF_INET6);
874 }
875 
876 static void
877 sbytes_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 
903 ATF_TC(sbytes_positive_v4);
904 ATF_TC_HEAD(sbytes_positive_v4, tc)
905 {
906 
907 	atf_tc_set_md_var(tc, "descr",
908 	    "Verify positive `sbytes` functionality (IPv4)");
909 }
910 ATF_TC_BODY(sbytes_positive_v4, tc)
911 {
912 
913 	sbytes_positive_test(AF_INET);
914 }
915 
916 ATF_TC(sbytes_positive_v6);
917 ATF_TC_HEAD(sbytes_positive_v6, tc)
918 {
919 
920 	atf_tc_set_md_var(tc, "descr",
921 	    "Verify positive `sbytes` functionality (IPv6)");
922 }
923 ATF_TC_BODY(sbytes_positive_v6, tc)
924 {
925 
926 	sbytes_positive_test(AF_INET6);
927 }
928 
929 static void
930 sbytes_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 
954 ATF_TC(sbytes_negative_v4);
955 ATF_TC_HEAD(sbytes_negative_v4, tc)
956 {
957 
958 	atf_tc_set_md_var(tc, "descr",
959 	    "Verify negative `sbytes` functionality (IPv4)");
960 }
961 ATF_TC_BODY(sbytes_negative_v4, tc)
962 {
963 
964 	sbytes_negative_test(AF_INET);
965 }
966 
967 ATF_TC(sbytes_negative_v6);
968 ATF_TC_HEAD(sbytes_negative_v6, tc)
969 {
970 
971 	atf_tc_set_md_var(tc, "descr",
972 	    "Verify negative `sbytes` functionality (IPv6)");
973 }
974 ATF_TC_BODY(sbytes_negative_v6, tc)
975 {
976 
977 	sbytes_negative_test(AF_INET6);
978 }
979 
980 static void
981 s_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 
998 ATF_TC(s_negative_not_connected_socket_v4);
999 ATF_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 
1006 ATF_TC_BODY(s_negative_not_connected_socket_v4, tc)
1007 {
1008 
1009 	s_negative_not_connected_socket_test(AF_INET);
1010 }
1011 
1012 ATF_TC(s_negative_not_connected_socket_v6);
1013 ATF_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 
1020 ATF_TC_BODY(s_negative_not_connected_socket_v6, tc)
1021 {
1022 
1023 	s_negative_not_connected_socket_test(AF_INET6);
1024 }
1025 
1026 ATF_TC(s_negative_not_descriptor);
1027 ATF_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 
1034 ATF_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 
1049 ATF_TC(s_negative_not_socket_file_descriptor);
1050 ATF_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 
1057 ATF_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 
1074 static void
1075 s_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 
1092 ATF_TC(s_negative_udp_socket_v4);
1093 ATF_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 }
1099 ATF_TC_BODY(s_negative_udp_socket_v4, tc)
1100 {
1101 
1102 	s_negative_udp_socket_test(AF_INET);
1103 }
1104 
1105 ATF_TC(s_negative_udp_socket_v6);
1106 ATF_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 }
1112 ATF_TC_BODY(s_negative_udp_socket_v6, tc)
1113 {
1114 
1115 	s_negative_udp_socket_test(AF_INET6);
1116 }
1117 
1118 ATF_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