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