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 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 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 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 382 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 383 384 fd_positive_file_test(AF_INET); 385 } 386 387 ATF_TC(fd_positive_file_v6); 388 ATF_TC_HEAD(fd_positive_file_v6, tc) 389 { 390 391 atf_tc_set_md_var(tc, "descr", 392 "Verify regular file as file descriptor support (IPv6)"); 393 } 394 ATF_TC_BODY(fd_positive_file_v6, tc) 395 { 396 397 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 398 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 399 400 fd_positive_file_test(AF_INET6); 401 } 402 403 static void 404 fd_positive_shm_test(int domain) 405 { 406 char *shm_pointer; 407 off_t offset; 408 size_t nbytes, pattern_size; 409 pid_t server_pid; 410 int client_sock, error, fd, port, server_sock; 411 412 pattern_size = strlen(DETERMINISTIC_PATTERN); 413 414 printf("pattern size: %zu\n", pattern_size); 415 416 fd = shm_open(SHM_ANON, O_RDWR|O_CREAT, 0600); 417 ATF_REQUIRE_MSG(fd != -1, "shm_open failed: %s", strerror(errno)); 418 ATF_REQUIRE_EQ_MSG(0, ftruncate(fd, pattern_size), 419 "ftruncate failed: %s", strerror(errno)); 420 shm_pointer = mmap(NULL, pattern_size, PROT_READ|PROT_WRITE, 421 MAP_SHARED, fd, 0); 422 ATF_REQUIRE_MSG(shm_pointer != MAP_FAILED, 423 "mmap failed: %s", strerror(errno)); 424 memcpy(shm_pointer, DETERMINISTIC_PATTERN, pattern_size); 425 ATF_REQUIRE_EQ_MSG(0, 426 memcmp(shm_pointer, DETERMINISTIC_PATTERN, pattern_size), 427 "memcmp showed data mismatch: '%s' != '%s'", 428 DETERMINISTIC_PATTERN, shm_pointer); 429 430 port = generate_random_port(__LINE__ + domain); 431 server_sock = setup_tcp_server(domain, port); 432 client_sock = setup_tcp_client(domain, port); 433 434 server_pid = atf_utils_fork(); 435 if (server_pid == 0) { 436 (void)close(client_sock); 437 server_cat(DESTINATION_FILE, server_sock, pattern_size); 438 _exit(0); 439 } else 440 (void)close(server_sock); 441 442 nbytes = 0; 443 offset = 0; 444 error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL, 445 SF_FLAGS(0, 0)); 446 ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno)); 447 (void)close(client_sock); 448 449 atf_utils_wait(server_pid, 0, "", ""); 450 verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes); 451 452 (void)munmap(shm_pointer, sizeof(DETERMINISTIC_PATTERN)); 453 (void)close(fd); 454 } 455 456 ATF_TC(fd_positive_shm_v4); 457 ATF_TC_HEAD(fd_positive_shm_v4, tc) 458 { 459 460 atf_tc_set_md_var(tc, "descr", 461 "Verify shared memory as file descriptor support (IPv4)"); 462 } 463 ATF_TC_BODY(fd_positive_shm_v4, tc) 464 { 465 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 466 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 467 468 fd_positive_shm_test(AF_INET); 469 } 470 471 ATF_TC(fd_positive_shm_v6); 472 ATF_TC_HEAD(fd_positive_shm_v6, tc) 473 { 474 475 atf_tc_set_md_var(tc, "descr", 476 "Verify shared memory as file descriptor support (IPv6))"); 477 } 478 ATF_TC_BODY(fd_positive_shm_v6, tc) 479 { 480 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 481 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 482 483 fd_positive_shm_test(AF_INET6); 484 } 485 486 static void 487 fd_negative_bad_fd_test(int domain) 488 { 489 int client_sock, error, fd, port, server_sock; 490 491 port = generate_random_port(__LINE__ + domain); 492 server_sock = setup_tcp_server(domain, port); 493 client_sock = setup_tcp_client(domain, port); 494 495 fd = -1; 496 497 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 498 ATF_REQUIRE_ERRNO(EBADF, error == -1); 499 500 (void)close(client_sock); 501 (void)close(server_sock); 502 } 503 504 ATF_TC(fd_negative_bad_fd_v4); 505 ATF_TC_HEAD(fd_negative_bad_fd_v4, tc) 506 { 507 508 atf_tc_set_md_var(tc, "descr", 509 "Verify bad file descriptor returns EBADF (IPv4)"); 510 } 511 ATF_TC_BODY(fd_negative_bad_fd_v4, tc) 512 { 513 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 514 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 515 516 fd_negative_bad_fd_test(AF_INET); 517 } 518 519 ATF_TC(fd_negative_bad_fd_v6); 520 ATF_TC_HEAD(fd_negative_bad_fd_v6, tc) 521 { 522 523 atf_tc_set_md_var(tc, "descr", 524 "Verify bad file descriptor returns EBADF (IPv6)"); 525 } 526 ATF_TC_BODY(fd_negative_bad_fd_v6, tc) 527 { 528 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 529 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 530 531 fd_negative_bad_fd_test(AF_INET6); 532 } 533 534 static void 535 flags_test(int domain) 536 { 537 off_t offset; 538 size_t nbytes, pattern_size; 539 int client_sock, error, fd, i, port, server_sock; 540 pid_t server_pid; 541 int16_t number_pages = 10; 542 543 pattern_size = strlen(DETERMINISTIC_PATTERN); 544 545 struct testcase { 546 int16_t readahead_pages, flags; 547 } testcases[] = { 548 /* This is covered in `:fd_positive_file` */ 549 #if 0 550 { 551 .readahead_pages = 0, 552 .flags = 0 553 }, 554 #endif 555 { 556 .readahead_pages = 0, 557 .flags = SF_NOCACHE 558 }, 559 #ifdef SF_USER_READAHEAD 560 { 561 .readahead_pages = 0, 562 .flags = SF_NOCACHE|SF_USER_READAHEAD 563 }, 564 { 565 .readahead_pages = 0, 566 .flags = SF_USER_READAHEAD 567 }, 568 #endif 569 { 570 .readahead_pages = number_pages, 571 .flags = 0 572 }, 573 { 574 .readahead_pages = number_pages, 575 .flags = SF_NOCACHE 576 }, 577 #ifdef SF_USER_READAHEAD 578 { 579 .readahead_pages = number_pages, 580 .flags = SF_NOCACHE|SF_USER_READAHEAD 581 }, 582 #endif 583 { 584 .readahead_pages = number_pages, 585 .flags = SF_NOCACHE 586 }, 587 { 588 .readahead_pages = number_pages, 589 .flags = SF_NODISKIO 590 } 591 }; 592 593 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 594 for (i = 0; i < nitems(testcases); i++) { 595 fd = open(SOURCE_FILE, O_RDONLY); 596 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 597 598 port = generate_random_port(i * __LINE__ + domain); 599 server_sock = setup_tcp_server(domain, port); 600 client_sock = setup_tcp_client(domain, port); 601 602 server_pid = atf_utils_fork(); 603 if (server_pid == 0) { 604 (void)close(client_sock); 605 server_cat(DESTINATION_FILE, server_sock, pattern_size); 606 _exit(0); 607 } else 608 (void)close(server_sock); 609 610 nbytes = 0; 611 offset = 0; 612 error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL, 613 SF_FLAGS(testcases[i].readahead_pages, testcases[i].flags)); 614 ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s", 615 i, strerror(errno)); 616 (void)close(client_sock); 617 618 atf_utils_wait(server_pid, 0, "", ""); 619 verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes); 620 621 (void)close(fd); 622 } 623 } 624 625 ATF_TC(flags_v4); 626 ATF_TC_HEAD(flags_v4, tc) 627 { 628 629 atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv4)"); 630 } 631 ATF_TC_BODY(flags_v4, tc) 632 { 633 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 634 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 635 636 flags_test(AF_INET); 637 } 638 639 ATF_TC(flags_v6); 640 ATF_TC_HEAD(flags_v6, tc) 641 { 642 643 atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv6)"); 644 } 645 ATF_TC_BODY(flags_v6, tc) 646 { 647 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 648 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 649 650 flags_test(AF_INET6); 651 } 652 653 static void 654 hdtr_positive_test(int domain) 655 { 656 struct iovec headers[1], trailers[1]; 657 struct testcase { 658 bool include_headers, include_trailers; 659 } testcases[] = { 660 /* This is covered in `:fd_positive_file` */ 661 #if 0 662 { 663 .include_headers = false, 664 .include_trailers = false 665 }, 666 #endif 667 { 668 .include_headers = true, 669 .include_trailers = false 670 }, 671 { 672 .include_headers = false, 673 .include_trailers = true 674 }, 675 { 676 .include_headers = true, 677 .include_trailers = true 678 } 679 }; 680 off_t offset; 681 size_t nbytes; 682 int client_sock, error, fd, fd2, i, port, rc, server_sock; 683 pid_t server_pid; 684 685 headers[0].iov_base = "This is a header"; 686 headers[0].iov_len = strlen(headers[0].iov_base); 687 trailers[0].iov_base = "This is a trailer"; 688 trailers[0].iov_len = strlen(trailers[0].iov_base); 689 offset = 0; 690 nbytes = 0; 691 692 for (i = 0; i < nitems(testcases); i++) { 693 struct sf_hdtr hdtr; 694 char *pattern; 695 696 if (testcases[i].include_headers) { 697 hdtr.headers = headers; 698 hdtr.hdr_cnt = nitems(headers); 699 } else { 700 hdtr.headers = NULL; 701 hdtr.hdr_cnt = 0; 702 } 703 704 if (testcases[i].include_trailers) { 705 hdtr.trailers = trailers; 706 hdtr.trl_cnt = nitems(trailers); 707 } else { 708 hdtr.trailers = NULL; 709 hdtr.trl_cnt = 0; 710 } 711 712 port = generate_random_port(i * __LINE__ + domain); 713 server_sock = setup_tcp_server(domain, port); 714 client_sock = setup_tcp_client(domain, port); 715 716 rc = asprintf(&pattern, "%s%s%s", 717 testcases[i].include_headers ? (char *)headers[0].iov_base : "", 718 DETERMINISTIC_PATTERN, 719 testcases[i].include_trailers ? (char *)trailers[0].iov_base : ""); 720 ATF_REQUIRE_MSG(rc != -1, "asprintf failed: %s", strerror(errno)); 721 722 atf_utils_create_file(SOURCE_FILE ".full", "%s", pattern); 723 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 724 725 fd = open(SOURCE_FILE, O_RDONLY); 726 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 727 728 fd2 = open(SOURCE_FILE ".full", O_RDONLY); 729 ATF_REQUIRE_MSG(fd2 != -1, "open failed: %s", strerror(errno)); 730 731 server_pid = atf_utils_fork(); 732 if (server_pid == 0) { 733 (void)close(client_sock); 734 server_cat(DESTINATION_FILE, server_sock, 735 strlen(pattern)); 736 _exit(0); 737 } else 738 (void)close(server_sock); 739 740 error = sendfile(fd, client_sock, offset, nbytes, &hdtr, 741 NULL, SF_FLAGS(0, 0)); 742 ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s", 743 i, strerror(errno)); 744 (void)close(client_sock); 745 746 atf_utils_wait(server_pid, 0, "", ""); 747 verify_source_and_dest(DESTINATION_FILE, fd2, offset, nbytes); 748 749 (void)close(fd); 750 (void)close(fd2); 751 free(pattern); 752 pattern = NULL; 753 } 754 } 755 756 ATF_TC(hdtr_positive_v4); 757 ATF_TC_HEAD(hdtr_positive_v4, tc) 758 { 759 760 atf_tc_set_md_var(tc, "descr", 761 "Verify positive hdtr functionality (IPv4)"); 762 } 763 ATF_TC_BODY(hdtr_positive_v4, tc) 764 { 765 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 766 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 767 768 hdtr_positive_test(AF_INET); 769 } 770 771 ATF_TC(hdtr_positive_v6); 772 ATF_TC_HEAD(hdtr_positive_v6, tc) 773 { 774 775 atf_tc_set_md_var(tc, "descr", 776 "Verify positive hdtr functionality (IPv6)"); 777 } 778 ATF_TC_BODY(hdtr_positive_v6, tc) 779 { 780 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 781 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 782 783 hdtr_positive_test(AF_INET); 784 } 785 786 static void 787 hdtr_negative_bad_pointers_test(int domain) 788 { 789 int client_sock, error, fd, port, server_sock; 790 struct sf_hdtr *hdtr1, hdtr2, hdtr3; 791 792 port = generate_random_port(__LINE__ + domain); 793 794 hdtr1 = (struct sf_hdtr*)-1; 795 796 memset(&hdtr2, 0, sizeof(hdtr2)); 797 hdtr2.hdr_cnt = 1; 798 hdtr2.headers = (struct iovec*)-1; 799 800 memset(&hdtr3, 0, sizeof(hdtr3)); 801 hdtr3.trl_cnt = 1; 802 hdtr3.trailers = (struct iovec*)-1; 803 804 fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600); 805 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 806 807 server_sock = setup_tcp_server(domain, port); 808 client_sock = setup_tcp_client(domain, port); 809 810 error = sendfile(fd, client_sock, 0, 0, hdtr1, NULL, SF_FLAGS(0, 0)); 811 ATF_CHECK_ERRNO(EFAULT, error == -1); 812 813 error = sendfile(fd, client_sock, 0, 0, &hdtr2, NULL, SF_FLAGS(0, 0)); 814 ATF_CHECK_ERRNO(EFAULT, error == -1); 815 816 error = sendfile(fd, client_sock, 0, 0, &hdtr3, NULL, SF_FLAGS(0, 0)); 817 ATF_CHECK_ERRNO(EFAULT, error == -1); 818 819 (void)close(fd); 820 (void)close(client_sock); 821 (void)close(server_sock); 822 } 823 824 ATF_TC(hdtr_negative_bad_pointers_v4); 825 ATF_TC_HEAD(hdtr_negative_bad_pointers_v4, tc) 826 { 827 828 atf_tc_set_md_var(tc, "descr", 829 "Verify that bad pointers for hdtr storage result in EFAULT (IPv4)"); 830 } 831 ATF_TC_BODY(hdtr_negative_bad_pointers_v4, tc) 832 { 833 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 834 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 835 836 hdtr_negative_bad_pointers_test(AF_INET); 837 } 838 839 ATF_TC(hdtr_negative_bad_pointers_v6); 840 ATF_TC_HEAD(hdtr_negative_bad_pointers_v6, tc) 841 { 842 843 atf_tc_set_md_var(tc, "descr", 844 "Verify that bad pointers for hdtr storage result in EFAULT (IPv6)"); 845 } 846 ATF_TC_BODY(hdtr_negative_bad_pointers_v6, tc) 847 { 848 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 849 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 850 851 hdtr_negative_bad_pointers_test(AF_INET6); 852 } 853 854 static void 855 offset_negative_value_less_than_zero_test(int domain) 856 { 857 int client_sock, error, fd, port, server_sock; 858 859 port = generate_random_port(__LINE__ + domain); 860 server_sock = setup_tcp_server(domain, port); 861 client_sock = setup_tcp_client(domain, port); 862 863 fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600); 864 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 865 866 error = sendfile(fd, client_sock, -1, 0, NULL, NULL, SF_FLAGS(0, 0)); 867 ATF_REQUIRE_ERRNO(EINVAL, error == -1); 868 869 (void)close(fd); 870 (void)close(client_sock); 871 (void)close(server_sock); 872 } 873 874 ATF_TC(offset_negative_value_less_than_zero_v4); 875 ATF_TC_HEAD(offset_negative_value_less_than_zero_v4, tc) 876 { 877 878 atf_tc_set_md_var(tc, "descr", 879 "Verify that a negative offset results in EINVAL (IPv4)"); 880 } 881 ATF_TC_BODY(offset_negative_value_less_than_zero_v4, tc) 882 { 883 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 884 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 885 886 offset_negative_value_less_than_zero_test(AF_INET); 887 } 888 889 ATF_TC(offset_negative_value_less_than_zero_v6); 890 ATF_TC_HEAD(offset_negative_value_less_than_zero_v6, tc) 891 { 892 893 atf_tc_set_md_var(tc, "descr", 894 "Verify that a negative offset results in EINVAL (IPv6)"); 895 } 896 ATF_TC_BODY(offset_negative_value_less_than_zero_v6, tc) 897 { 898 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 899 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 900 901 offset_negative_value_less_than_zero_test(AF_INET6); 902 } 903 904 static void 905 sbytes_positive_test(int domain) 906 { 907 size_t pattern_size = strlen(DETERMINISTIC_PATTERN); 908 off_t sbytes; 909 int client_sock, error, fd, port, server_sock; 910 911 port = generate_random_port(__LINE__ + domain); 912 server_sock = setup_tcp_server(domain, port); 913 client_sock = setup_tcp_client(domain, port); 914 915 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 916 fd = open(SOURCE_FILE, O_RDONLY); 917 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 918 919 error = sendfile(fd, client_sock, 0, 0, NULL, &sbytes, SF_FLAGS(0, 0)); 920 ATF_CHECK_EQ_MSG(error, 0, "sendfile failed: %s", strerror(errno)); 921 922 (void)close(fd); 923 (void)close(client_sock); 924 (void)close(server_sock); 925 926 ATF_CHECK_EQ_MSG(pattern_size, sbytes, 927 "the value returned by sbytes does not match the expected pattern " 928 "size"); 929 } 930 931 ATF_TC(sbytes_positive_v4); 932 ATF_TC_HEAD(sbytes_positive_v4, tc) 933 { 934 935 atf_tc_set_md_var(tc, "descr", 936 "Verify positive `sbytes` functionality (IPv4)"); 937 } 938 ATF_TC_BODY(sbytes_positive_v4, tc) 939 { 940 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 941 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 942 943 sbytes_positive_test(AF_INET); 944 } 945 946 ATF_TC(sbytes_positive_v6); 947 ATF_TC_HEAD(sbytes_positive_v6, tc) 948 { 949 950 atf_tc_set_md_var(tc, "descr", 951 "Verify positive `sbytes` functionality (IPv6)"); 952 } 953 ATF_TC_BODY(sbytes_positive_v6, tc) 954 { 955 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 956 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 957 958 sbytes_positive_test(AF_INET6); 959 } 960 961 static void 962 sbytes_negative_test(int domain) 963 { 964 off_t *sbytes_p = (off_t*)-1; 965 int client_sock, error, fd, port, server_sock; 966 967 port = generate_random_port(__LINE__ + domain); 968 server_sock = setup_tcp_server(domain, port); 969 client_sock = setup_tcp_client(domain, port); 970 971 atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN); 972 fd = open(SOURCE_FILE, O_RDONLY); 973 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 974 975 atf_tc_expect_fail( 976 "bug 232210: EFAULT assert fails because copyout(9) call is not checked"); 977 978 error = sendfile(fd, client_sock, 0, 0, NULL, sbytes_p, SF_FLAGS(0, 0)); 979 ATF_REQUIRE_ERRNO(EFAULT, error == -1); 980 981 (void)close(fd); 982 (void)close(client_sock); 983 (void)close(server_sock); 984 } 985 986 ATF_TC(sbytes_negative_v4); 987 ATF_TC_HEAD(sbytes_negative_v4, tc) 988 { 989 990 atf_tc_set_md_var(tc, "descr", 991 "Verify negative `sbytes` functionality (IPv4)"); 992 } 993 ATF_TC_BODY(sbytes_negative_v4, tc) 994 { 995 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 996 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 997 998 sbytes_negative_test(AF_INET); 999 } 1000 1001 ATF_TC(sbytes_negative_v6); 1002 ATF_TC_HEAD(sbytes_negative_v6, tc) 1003 { 1004 1005 atf_tc_set_md_var(tc, "descr", 1006 "Verify negative `sbytes` functionality (IPv6)"); 1007 } 1008 ATF_TC_BODY(sbytes_negative_v6, tc) 1009 { 1010 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 1011 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 1012 1013 sbytes_negative_test(AF_INET6); 1014 } 1015 1016 static void 1017 s_negative_not_connected_socket_test(int domain) 1018 { 1019 int client_sock, error, fd, port; 1020 1021 port = generate_random_port(__LINE__ + domain); 1022 client_sock = setup_tcp_server(domain, port); 1023 1024 fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600); 1025 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 1026 1027 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 1028 ATF_REQUIRE_ERRNO(ENOTCONN, error == -1); 1029 1030 (void)close(fd); 1031 (void)close(client_sock); 1032 } 1033 1034 ATF_TC(s_negative_not_connected_socket_v4); 1035 ATF_TC_HEAD(s_negative_not_connected_socket_v4, tc) 1036 { 1037 1038 atf_tc_set_md_var(tc, "descr", 1039 "Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv4)"); 1040 } 1041 1042 ATF_TC_BODY(s_negative_not_connected_socket_v4, tc) 1043 { 1044 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 1045 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 1046 1047 s_negative_not_connected_socket_test(AF_INET); 1048 } 1049 1050 ATF_TC(s_negative_not_connected_socket_v6); 1051 ATF_TC_HEAD(s_negative_not_connected_socket_v6, tc) 1052 { 1053 1054 atf_tc_set_md_var(tc, "descr", 1055 "Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv6)"); 1056 } 1057 1058 ATF_TC_BODY(s_negative_not_connected_socket_v6, tc) 1059 { 1060 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 1061 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 1062 1063 s_negative_not_connected_socket_test(AF_INET6); 1064 } 1065 1066 ATF_TC(s_negative_not_descriptor); 1067 ATF_TC_HEAD(s_negative_not_descriptor, tc) 1068 { 1069 1070 atf_tc_set_md_var(tc, "descr", 1071 "Verify that an invalid file descriptor, e.g., -1, fails with EBADF"); 1072 } 1073 1074 ATF_TC_BODY(s_negative_not_descriptor, tc) 1075 { 1076 int client_sock, error, fd; 1077 1078 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 1079 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 1080 1081 client_sock = -1; 1082 1083 fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600); 1084 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 1085 1086 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 1087 ATF_REQUIRE_ERRNO(EBADF, error == -1); 1088 1089 (void)close(fd); 1090 } 1091 1092 ATF_TC(s_negative_not_socket_file_descriptor); 1093 ATF_TC_HEAD(s_negative_not_socket_file_descriptor, tc) 1094 { 1095 1096 atf_tc_set_md_var(tc, "descr", 1097 "Verify that a non-socket file descriptor fails with ENOTSOCK"); 1098 } 1099 1100 ATF_TC_BODY(s_negative_not_socket_file_descriptor, tc) 1101 { 1102 int client_sock, error, fd; 1103 1104 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 1105 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 1106 1107 fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600); 1108 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 1109 1110 client_sock = open(_PATH_DEVNULL, O_WRONLY); 1111 ATF_REQUIRE_MSG(client_sock != -1, "open failed: %s", strerror(errno)); 1112 1113 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 1114 ATF_REQUIRE_ERRNO(ENOTSOCK, error == -1); 1115 1116 (void)close(fd); 1117 (void)close(client_sock); 1118 } 1119 1120 static void 1121 s_negative_udp_socket_test(int domain) 1122 { 1123 int client_sock, error, fd, port; 1124 1125 port = generate_random_port(__LINE__ + domain); 1126 client_sock = setup_client(domain, SOCK_DGRAM, port); 1127 1128 fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600); 1129 ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); 1130 1131 error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0)); 1132 ATF_REQUIRE_ERRNO(EINVAL, error == -1); 1133 1134 (void)close(fd); 1135 (void)close(client_sock); 1136 } 1137 1138 ATF_TC(s_negative_udp_socket_v4); 1139 ATF_TC_HEAD(s_negative_udp_socket_v4, tc) 1140 { 1141 1142 atf_tc_set_md_var(tc, "descr", 1143 "Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv4)"); 1144 } 1145 ATF_TC_BODY(s_negative_udp_socket_v4, tc) 1146 { 1147 1148 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 1149 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 1150 1151 s_negative_udp_socket_test(AF_INET); 1152 } 1153 1154 ATF_TC(s_negative_udp_socket_v6); 1155 ATF_TC_HEAD(s_negative_udp_socket_v6, tc) 1156 { 1157 1158 atf_tc_set_md_var(tc, "descr", 1159 "Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv6)"); 1160 } 1161 ATF_TC_BODY(s_negative_udp_socket_v6, tc) 1162 { 1163 1164 if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false)) 1165 atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25"); 1166 1167 s_negative_udp_socket_test(AF_INET6); 1168 } 1169 1170 ATF_TP_ADD_TCS(tp) 1171 { 1172 1173 ATF_TP_ADD_TC(tp, fd_positive_file_v4); 1174 ATF_TP_ADD_TC(tp, fd_positive_file_v6); 1175 ATF_TP_ADD_TC(tp, fd_positive_shm_v4); 1176 ATF_TP_ADD_TC(tp, fd_positive_shm_v6); 1177 ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v4); 1178 ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v6); 1179 ATF_TP_ADD_TC(tp, flags_v4); 1180 ATF_TP_ADD_TC(tp, flags_v6); 1181 /* 1182 * TODO: the negative case for SF_NODISKIO (returns EBUSY if file in 1183 * use) is not covered yet. 1184 * 1185 * Need to lock a file in a subprocess in write mode, then try and 1186 * send the data in read mode with sendfile. 1187 * 1188 * This should work with FFS/UFS, but there are no guarantees about 1189 * other filesystem implementations of sendfile(2), e.g., ZFS. 1190 */ 1191 ATF_TP_ADD_TC(tp, hdtr_positive_v4); 1192 ATF_TP_ADD_TC(tp, hdtr_positive_v6); 1193 ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v4); 1194 ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v6); 1195 ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v4); 1196 ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v6); 1197 ATF_TP_ADD_TC(tp, sbytes_positive_v4); 1198 ATF_TP_ADD_TC(tp, sbytes_positive_v6); 1199 ATF_TP_ADD_TC(tp, sbytes_negative_v4); 1200 ATF_TP_ADD_TC(tp, sbytes_negative_v6); 1201 ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v4); 1202 ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v6); 1203 ATF_TP_ADD_TC(tp, s_negative_not_descriptor); 1204 ATF_TP_ADD_TC(tp, s_negative_not_socket_file_descriptor); 1205 ATF_TP_ADD_TC(tp, s_negative_udp_socket_v4); 1206 ATF_TP_ADD_TC(tp, s_negative_udp_socket_v6); 1207 1208 return (atf_no_error()); 1209 } 1210