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 #include <sys/param.h>
29 #include <sys/mman.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/sysctl.h>
33 #include <sys/uio.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <netdb.h>
38 #include <paths.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #include <atf-c.h>
45
46 const char DETERMINISTIC_PATTERN[] =
47 "The past is already gone, the future is not yet here. There's only one moment for you to live.\n";
48
49 #define SOURCE_FILE "source"
50 #define DESTINATION_FILE "dest"
51
52 #define PORTRANGE_FIRST "net.inet.ip.portrange.first"
53 #define PORTRANGE_LAST "net.inet.ip.portrange.last"
54
55 static int portrange_first, portrange_last;
56
57 static int
get_int_via_sysctlbyname(const char * oidname)58 get_int_via_sysctlbyname(const char *oidname)
59 {
60 size_t oldlen;
61 int int_value;
62
63 oldlen = sizeof(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
generate_random_port(int seed)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
resolve_localhost(struct addrinfo ** res,int domain,int type,int port)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
make_socket(int domain,int type,int protocol)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
setup_client(int domain,int type,int port)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
setup_server(int domain,int type,int port)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
server_cat(const char * dest_filename,int server_sock,size_t len)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
setup_tcp_server(int domain,int port)252 setup_tcp_server(int domain, int port)
253 {
254
255 return (setup_server(domain, SOCK_STREAM, port));
256 }
257
258 static int
setup_tcp_client(int domain,int port)259 setup_tcp_client(int domain, int port)
260 {
261
262 return (setup_client(domain, SOCK_STREAM, port));
263 }
264
265 static off_t
file_size_from_fd(int fd)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
verify_source_and_dest(const char * dest_filename,int src_fd,off_t offset,size_t nbytes)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
fd_positive_file_test(int domain)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);
ATF_TC_HEAD(fd_positive_file_v4,tc)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 }
ATF_TC_BODY(fd_positive_file_v4,tc)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);
ATF_TC_HEAD(fd_positive_file_v6,tc)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 }
ATF_TC_BODY(fd_positive_file_v6,tc)391 ATF_TC_BODY(fd_positive_file_v6, tc)
392 {
393
394 fd_positive_file_test(AF_INET6);
395 }
396
397 static void
fd_positive_shm_test(int domain)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);
ATF_TC_HEAD(fd_positive_shm_v4,tc)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 }
ATF_TC_BODY(fd_positive_shm_v4,tc)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);
ATF_TC_HEAD(fd_positive_shm_v6,tc)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 }
ATF_TC_BODY(fd_positive_shm_v6,tc)470 ATF_TC_BODY(fd_positive_shm_v6, tc)
471 {
472
473 fd_positive_shm_test(AF_INET6);
474 }
475
476 static void
fd_negative_bad_fd_test(int domain)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);
ATF_TC_HEAD(fd_negative_bad_fd_v4,tc)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 }
ATF_TC_BODY(fd_negative_bad_fd_v4,tc)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);
ATF_TC_HEAD(fd_negative_bad_fd_v6,tc)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 }
ATF_TC_BODY(fd_negative_bad_fd_v6,tc)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
flags_test(int domain)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);
ATF_TC_HEAD(flags_v4,tc)612 ATF_TC_HEAD(flags_v4, tc)
613 {
614
615 atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv4)");
616 }
ATF_TC_BODY(flags_v4,tc)617 ATF_TC_BODY(flags_v4, tc)
618 {
619
620 flags_test(AF_INET);
621 }
622
623 ATF_TC(flags_v6);
ATF_TC_HEAD(flags_v6,tc)624 ATF_TC_HEAD(flags_v6, tc)
625 {
626
627 atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv6)");
628 }
ATF_TC_BODY(flags_v6,tc)629 ATF_TC_BODY(flags_v6, tc)
630 {
631
632 flags_test(AF_INET6);
633 }
634
635 static void
hdtr_positive_test(int domain)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);
ATF_TC_HEAD(hdtr_positive_v4,tc)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 }
ATF_TC_BODY(hdtr_positive_v4,tc)745 ATF_TC_BODY(hdtr_positive_v4, tc)
746 {
747
748 hdtr_positive_test(AF_INET);
749 }
750
751 ATF_TC(hdtr_positive_v6);
ATF_TC_HEAD(hdtr_positive_v6,tc)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 }
ATF_TC_BODY(hdtr_positive_v6,tc)758 ATF_TC_BODY(hdtr_positive_v6, tc)
759 {
760
761 hdtr_positive_test(AF_INET);
762 }
763
764 static void
hdtr_negative_bad_pointers_test(int domain)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, 0600);
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);
ATF_TC_HEAD(hdtr_negative_bad_pointers_v4,tc)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 }
ATF_TC_BODY(hdtr_negative_bad_pointers_v4,tc)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);
ATF_TC_HEAD(hdtr_negative_bad_pointers_v6,tc)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 }
ATF_TC_BODY(hdtr_negative_bad_pointers_v6,tc)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
offset_negative_value_less_than_zero_test(int domain)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, 0600);
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);
ATF_TC_HEAD(offset_negative_value_less_than_zero_v4,tc)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 }
ATF_TC_BODY(offset_negative_value_less_than_zero_v4,tc)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);
ATF_TC_HEAD(offset_negative_value_less_than_zero_v6,tc)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 }
ATF_TC_BODY(offset_negative_value_less_than_zero_v6,tc)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
sbytes_positive_test(int domain)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);
ATF_TC_HEAD(sbytes_positive_v4,tc)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 }
ATF_TC_BODY(sbytes_positive_v4,tc)908 ATF_TC_BODY(sbytes_positive_v4, tc)
909 {
910
911 sbytes_positive_test(AF_INET);
912 }
913
914 ATF_TC(sbytes_positive_v6);
ATF_TC_HEAD(sbytes_positive_v6,tc)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 }
ATF_TC_BODY(sbytes_positive_v6,tc)921 ATF_TC_BODY(sbytes_positive_v6, tc)
922 {
923
924 sbytes_positive_test(AF_INET6);
925 }
926
927 static void
sbytes_negative_test(int domain)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);
ATF_TC_HEAD(sbytes_negative_v4,tc)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 }
ATF_TC_BODY(sbytes_negative_v4,tc)959 ATF_TC_BODY(sbytes_negative_v4, tc)
960 {
961
962 sbytes_negative_test(AF_INET);
963 }
964
965 ATF_TC(sbytes_negative_v6);
ATF_TC_HEAD(sbytes_negative_v6,tc)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 }
ATF_TC_BODY(sbytes_negative_v6,tc)972 ATF_TC_BODY(sbytes_negative_v6, tc)
973 {
974
975 sbytes_negative_test(AF_INET6);
976 }
977
978 static void
s_negative_not_connected_socket_test(int domain)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, 0600);
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);
ATF_TC_HEAD(s_negative_not_connected_socket_v4,tc)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
ATF_TC_BODY(s_negative_not_connected_socket_v4,tc)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);
ATF_TC_HEAD(s_negative_not_connected_socket_v6,tc)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
ATF_TC_BODY(s_negative_not_connected_socket_v6,tc)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);
ATF_TC_HEAD(s_negative_not_descriptor,tc)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
ATF_TC_BODY(s_negative_not_descriptor,tc)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, 0600);
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);
ATF_TC_HEAD(s_negative_not_socket_file_descriptor,tc)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
ATF_TC_BODY(s_negative_not_socket_file_descriptor,tc)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, 0600);
1060 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1061
1062 client_sock = open(_PATH_DEVNULL, O_WRONLY);
1063 ATF_REQUIRE_MSG(client_sock != -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
s_negative_udp_socket_test(int domain)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, 0600);
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);
ATF_TC_HEAD(s_negative_udp_socket_v4,tc)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 }
ATF_TC_BODY(s_negative_udp_socket_v4,tc)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);
ATF_TC_HEAD(s_negative_udp_socket_v6,tc)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 }
ATF_TC_BODY(s_negative_udp_socket_v6,tc)1110 ATF_TC_BODY(s_negative_udp_socket_v6, tc)
1111 {
1112
1113 s_negative_udp_socket_test(AF_INET6);
1114 }
1115
ATF_TP_ADD_TCS(tp)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