1 /*- 2 * Copyright (c) 2006 Robert N. M. Watson 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 * $FreeBSD$ 27 */ 28 29 #include <sys/types.h> 30 #include <sys/socket.h> 31 #include <sys/stat.h> 32 #include <sys/wait.h> 33 34 #include <netinet/in.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <limits.h> 39 #include <md5.h> 40 #include <signal.h> 41 #include <stdint.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 /* 48 * Simple regression test for sendfile. Creates a file sized at four pages 49 * and then proceeds to send it over a series of sockets, exercising a number 50 * of cases and performing limited validation. 51 */ 52 53 #define FAIL(msg) {printf("# %s\n", msg); \ 54 return (-1);} 55 56 #define FAIL_ERR(msg) {printf("# %s: %s\n", msg, strerror(errno)); \ 57 return (-1);} 58 59 #define TEST_PORT 5678 60 #define TEST_MAGIC 0x4440f7bb 61 #define TEST_PAGES 4 62 #define TEST_SECONDS 30 63 64 struct test_header { 65 uint32_t th_magic; 66 uint32_t th_header_length; 67 uint32_t th_offset; 68 uint32_t th_length; 69 char th_md5[33]; 70 }; 71 72 struct sendfile_test { 73 uint32_t hdr_length; 74 uint32_t offset; 75 uint32_t length; 76 }; 77 78 int file_fd; 79 char path[PATH_MAX]; 80 int listen_socket; 81 int accept_socket; 82 83 static int test_th(struct test_header *th, uint32_t *header_length, 84 uint32_t *offset, uint32_t *length); 85 static void signal_alarm(int signum); 86 static void setup_alarm(int seconds); 87 static void cancel_alarm(void); 88 static int receive_test(void); 89 static void run_child(void); 90 static int new_test_socket(int *connect_socket); 91 static void init_th(struct test_header *th, uint32_t header_length, 92 uint32_t offset, uint32_t length); 93 static int send_test(int connect_socket, struct sendfile_test); 94 static void run_parent(void); 95 static void cleanup(void); 96 97 98 static int 99 test_th(struct test_header *th, uint32_t *header_length, uint32_t *offset, 100 uint32_t *length) 101 { 102 103 if (th->th_magic != htonl(TEST_MAGIC)) 104 FAIL("magic number not found in header") 105 *header_length = ntohl(th->th_header_length); 106 *offset = ntohl(th->th_offset); 107 *length = ntohl(th->th_length); 108 return (0); 109 } 110 111 static void 112 signal_alarm(int signum) 113 { 114 (void)signum; 115 116 printf("# test timeout\n"); 117 118 if (accept_socket > 0) 119 close(accept_socket); 120 if (listen_socket > 0) 121 close(listen_socket); 122 123 _exit(-1); 124 } 125 126 static void 127 setup_alarm(int seconds) 128 { 129 struct itimerval itv; 130 bzero(&itv, sizeof(itv)); 131 (void)seconds; 132 itv.it_value.tv_sec = seconds; 133 134 signal(SIGALRM, signal_alarm); 135 setitimer(ITIMER_REAL, &itv, NULL); 136 } 137 138 static void 139 cancel_alarm(void) 140 { 141 struct itimerval itv; 142 bzero(&itv, sizeof(itv)); 143 setitimer(ITIMER_REAL, &itv, NULL); 144 } 145 146 static int 147 receive_test(void) 148 { 149 uint32_t header_length, offset, length, counter; 150 struct test_header th; 151 ssize_t len; 152 char buf[10240]; 153 MD5_CTX md5ctx; 154 char *rxmd5; 155 156 len = read(accept_socket, &th, sizeof(th)); 157 if (len < 0 || (size_t)len < sizeof(th)) 158 FAIL_ERR("read") 159 160 if (test_th(&th, &header_length, &offset, &length) != 0) 161 return (-1); 162 163 MD5Init(&md5ctx); 164 165 counter = 0; 166 while (1) { 167 len = read(accept_socket, buf, sizeof(buf)); 168 if (len < 0 || len == 0) 169 break; 170 counter += len; 171 MD5Update(&md5ctx, buf, len); 172 } 173 174 rxmd5 = MD5End(&md5ctx, NULL); 175 176 if ((counter != header_length+length) || 177 memcmp(th.th_md5, rxmd5, 33) != 0) 178 FAIL("receive length mismatch") 179 180 free(rxmd5); 181 return (0); 182 } 183 184 static void 185 run_child(void) 186 { 187 struct sockaddr_in sin; 188 int rc = 0; 189 190 listen_socket = socket(PF_INET, SOCK_STREAM, 0); 191 if (listen_socket < 0) { 192 printf("# socket: %s\n", strerror(errno)); 193 rc = -1; 194 } 195 196 if (!rc) { 197 bzero(&sin, sizeof(sin)); 198 sin.sin_len = sizeof(sin); 199 sin.sin_family = AF_INET; 200 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 201 sin.sin_port = htons(TEST_PORT); 202 203 if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 204 printf("# bind: %s\n", strerror(errno)); 205 rc = -1; 206 } 207 } 208 209 if (!rc && listen(listen_socket, -1) < 0) { 210 printf("# listen: %s\n", strerror(errno)); 211 rc = -1; 212 } 213 214 if (!rc) { 215 accept_socket = accept(listen_socket, NULL, NULL); 216 setup_alarm(TEST_SECONDS); 217 if (receive_test() != 0) 218 rc = -1; 219 } 220 221 cancel_alarm(); 222 if (accept_socket > 0) 223 close(accept_socket); 224 if (listen_socket > 0) 225 close(listen_socket); 226 227 _exit(rc); 228 } 229 230 static int 231 new_test_socket(int *connect_socket) 232 { 233 struct sockaddr_in sin; 234 int rc = 0; 235 236 *connect_socket = socket(PF_INET, SOCK_STREAM, 0); 237 if (*connect_socket < 0) 238 FAIL_ERR("socket") 239 240 bzero(&sin, sizeof(sin)); 241 sin.sin_len = sizeof(sin); 242 sin.sin_family = AF_INET; 243 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 244 sin.sin_port = htons(TEST_PORT); 245 246 if (connect(*connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) 247 FAIL_ERR("connect") 248 249 return (rc); 250 } 251 252 static void 253 init_th(struct test_header *th, uint32_t header_length, uint32_t offset, 254 uint32_t length) 255 { 256 bzero(th, sizeof(*th)); 257 th->th_magic = htonl(TEST_MAGIC); 258 th->th_header_length = htonl(header_length); 259 th->th_offset = htonl(offset); 260 th->th_length = htonl(length); 261 262 MD5FileChunk(path, th->th_md5, offset, length); 263 } 264 265 static int 266 send_test(int connect_socket, struct sendfile_test test) 267 { 268 struct test_header th; 269 struct sf_hdtr hdtr, *hdtrp; 270 struct iovec headers; 271 char *header; 272 ssize_t len; 273 int length; 274 off_t off; 275 276 len = lseek(file_fd, 0, SEEK_SET); 277 if (len != 0) 278 FAIL_ERR("lseek") 279 280 if (test.length == 0) { 281 struct stat st; 282 if (fstat(file_fd, &st) < 0) 283 FAIL_ERR("fstat") 284 length = st.st_size - test.offset; 285 } 286 else { 287 length = test.length; 288 } 289 290 init_th(&th, test.hdr_length, test.offset, length); 291 292 len = write(connect_socket, &th, sizeof(th)); 293 if (len != sizeof(th)) 294 return (-1); 295 296 if (test.hdr_length != 0) { 297 header = malloc(test.hdr_length); 298 if (header == NULL) 299 FAIL_ERR("malloc") 300 301 hdtrp = &hdtr; 302 bzero(&headers, sizeof(headers)); 303 headers.iov_base = header; 304 headers.iov_len = test.hdr_length; 305 bzero(&hdtr, sizeof(hdtr)); 306 hdtr.headers = &headers; 307 hdtr.hdr_cnt = 1; 308 hdtr.trailers = NULL; 309 hdtr.trl_cnt = 0; 310 } else { 311 hdtrp = NULL; 312 header = NULL; 313 } 314 315 if (sendfile(file_fd, connect_socket, test.offset, test.length, 316 hdtrp, &off, 0) < 0) { 317 if (header != NULL) 318 free(header); 319 FAIL_ERR("sendfile") 320 } 321 322 if (length == 0) { 323 struct stat sb; 324 325 if (fstat(file_fd, &sb) == 0) 326 length = sb.st_size - test.offset; 327 } 328 329 if (header != NULL) 330 free(header); 331 332 if (off != length) 333 FAIL("offset != length") 334 335 return (0); 336 } 337 338 static void 339 run_parent(void) 340 { 341 int connect_socket; 342 int status; 343 int test_num; 344 int pid; 345 346 const int pagesize = getpagesize(); 347 348 struct sendfile_test tests[10] = { 349 { .hdr_length = 0, .offset = 0, .length = 1 }, 350 { .hdr_length = 0, .offset = 0, .length = pagesize }, 351 { .hdr_length = 0, .offset = 1, .length = 1 }, 352 { .hdr_length = 0, .offset = 1, .length = pagesize }, 353 { .hdr_length = 0, .offset = pagesize, .length = pagesize }, 354 { .hdr_length = 0, .offset = 0, .length = 2*pagesize }, 355 { .hdr_length = 0, .offset = 0, .length = 0 }, 356 { .hdr_length = 0, .offset = pagesize, .length = 0 }, 357 { .hdr_length = 0, .offset = 2*pagesize, .length = 0 }, 358 { .hdr_length = 0, .offset = TEST_PAGES*pagesize, .length = 0 } 359 }; 360 361 printf("1..10\n"); 362 363 for (test_num = 1; test_num <= 10; test_num++) { 364 365 pid = fork(); 366 if (pid == -1) { 367 printf("not ok %d\n", test_num); 368 continue; 369 } 370 371 if (pid == 0) 372 run_child(); 373 374 usleep(250000); 375 376 if (new_test_socket(&connect_socket) != 0) { 377 printf("not ok %d\n", test_num); 378 kill(pid, SIGALRM); 379 close(connect_socket); 380 continue; 381 } 382 383 if (send_test(connect_socket, tests[test_num-1]) != 0) { 384 printf("not ok %d\n", test_num); 385 kill(pid, SIGALRM); 386 close(connect_socket); 387 continue; 388 } 389 390 close(connect_socket); 391 if (waitpid(pid, &status, 0) == pid) { 392 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 393 printf("%s %d\n", "ok", test_num); 394 else 395 printf("%s %d\n", "not ok", test_num); 396 } 397 else { 398 printf("not ok %d\n", test_num); 399 } 400 } 401 } 402 403 static void 404 cleanup(void) 405 { 406 if (*path != '\0') 407 unlink(path); 408 } 409 410 int 411 main(void) 412 { 413 char *page_buffer; 414 int pagesize; 415 ssize_t len; 416 417 *path = '\0'; 418 419 pagesize = getpagesize(); 420 page_buffer = malloc(TEST_PAGES * pagesize); 421 if (page_buffer == NULL) 422 FAIL_ERR("malloc") 423 bzero(page_buffer, TEST_PAGES * pagesize); 424 425 snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX"); 426 file_fd = mkstemp(path); 427 atexit(cleanup); 428 429 len = write(file_fd, page_buffer, TEST_PAGES * pagesize); 430 if (len < 0) 431 FAIL_ERR("write") 432 433 len = lseek(file_fd, 0, SEEK_SET); 434 if (len < 0) 435 FAIL_ERR("lseek") 436 if (len != 0) 437 FAIL("len != 0") 438 439 run_parent(); 440 return (0); 441 } 442