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