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 33 #include <netinet/in.h> 34 35 #include <err.h> 36 #include <limits.h> 37 #include <signal.h> 38 #include <stdint.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 /* 45 * Simple regression test for sendfile. Creates a file sized at three pages 46 * and then proceeds to send it over a series of sockets, exercising a number 47 * of cases and performing limited validation. 48 */ 49 50 #define TEST_PORT 5678 51 #define TEST_MAGIC 0x4440f7bb 52 #define TEST_PAGES 4 53 #define TEST_SECONDS 30 54 55 struct test_header { 56 u_int32_t th_magic; 57 u_int32_t th_header_length; 58 u_int32_t th_offset; 59 u_int32_t th_length; 60 }; 61 62 pid_t child_pid, parent_pid; 63 int listen_socket; 64 int file_fd; 65 66 static int 67 test_th(struct test_header *th, u_int32_t *header_length, u_int32_t *offset, 68 u_int32_t *length) 69 { 70 71 if (th->th_magic != htonl(TEST_MAGIC)) 72 return (0); 73 *header_length = ntohl(th->th_header_length); 74 *offset = ntohl(th->th_offset); 75 *length = ntohl(th->th_length); 76 return (1); 77 } 78 79 static void 80 signal_alarm(int signum) 81 { 82 83 (void)signum; 84 } 85 86 static void 87 setup_alarm(int seconds) 88 { 89 90 signal(SIGALRM, signal_alarm); 91 alarm(seconds); 92 } 93 94 static void 95 cancel_alarm(void) 96 { 97 98 alarm(0); 99 signal(SIGALRM, SIG_DFL); 100 } 101 102 static void 103 receive_test(int accept_socket) 104 { 105 u_int32_t header_length, offset, length, counter; 106 struct test_header th; 107 ssize_t len; 108 char ch; 109 110 len = read(accept_socket, &th, sizeof(th)); 111 if (len < 0) 112 err(1, "read"); 113 if ((size_t)len < sizeof(th)) 114 errx(1, "read: %zd", len); 115 116 if (test_th(&th, &header_length, &offset, &length) == 0) 117 errx(1, "test_th: bad"); 118 119 counter = 0; 120 while (1) { 121 len = read(accept_socket, &ch, sizeof(ch)); 122 if (len < 0) 123 err(1, "read"); 124 if (len == 0) 125 break; 126 counter++; 127 /* XXXRW: Validate byte here. */ 128 } 129 if (counter != header_length + length) 130 errx(1, "receive_test: expected (%d, %d) received %d", 131 header_length, length, counter); 132 } 133 134 static void 135 run_child(void) 136 { 137 int accept_socket; 138 139 while (1) { 140 accept_socket = accept(listen_socket, NULL, NULL); 141 setup_alarm(TEST_SECONDS); 142 receive_test(accept_socket); 143 cancel_alarm(); 144 close(accept_socket); 145 } 146 } 147 148 static int 149 new_test_socket(void) 150 { 151 struct sockaddr_in sin; 152 int connect_socket; 153 154 connect_socket = socket(PF_INET, SOCK_STREAM, 0); 155 if (connect_socket < 0) 156 err(1, "socket"); 157 158 bzero(&sin, sizeof(sin)); 159 sin.sin_len = sizeof(sin); 160 sin.sin_family = AF_INET; 161 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 162 sin.sin_port = htons(TEST_PORT); 163 164 if (connect(connect_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) 165 err(1, "connect"); 166 167 return (connect_socket); 168 } 169 170 static void 171 init_th(struct test_header *th, u_int32_t header_length, u_int32_t offset, 172 u_int32_t length) 173 { 174 175 bzero(th, sizeof(*th)); 176 th->th_magic = htonl(TEST_MAGIC); 177 th->th_header_length = htonl(header_length); 178 th->th_offset = htonl(offset); 179 th->th_length = htonl(length); 180 } 181 182 static void 183 send_test(int connect_socket, u_int32_t header_length, u_int32_t offset, 184 u_int32_t length) 185 { 186 struct test_header th; 187 struct sf_hdtr hdtr, *hdtrp; 188 struct iovec headers; 189 char *header; 190 ssize_t len; 191 off_t off; 192 193 len = lseek(file_fd, 0, SEEK_SET); 194 if (len < 0) 195 err(1, "lseek"); 196 if (len != 0) 197 errx(1, "lseek: %zd", len); 198 199 init_th(&th, header_length, offset, length); 200 201 len = write(connect_socket, &th, sizeof(th)); 202 if (len < 0) 203 err(1, "send"); 204 if (len != sizeof(th)) 205 err(1, "send: %zd", len); 206 207 if (header_length != 0) { 208 header = malloc(header_length); 209 if (header == NULL) 210 err(1, "malloc"); 211 hdtrp = &hdtr; 212 bzero(&headers, sizeof(headers)); 213 headers.iov_base = header; 214 headers.iov_len = header_length; 215 bzero(&hdtr, sizeof(hdtr)); 216 hdtr.headers = &headers; 217 hdtr.hdr_cnt = 1; 218 hdtr.trailers = NULL; 219 hdtr.trl_cnt = 0; 220 } else { 221 hdtrp = NULL; 222 header = NULL; 223 } 224 225 if (sendfile(file_fd, connect_socket, offset, length, hdtrp, &off, 226 0) < 0) 227 err(1, "sendfile"); 228 229 if (length == 0) { 230 struct stat sb; 231 232 if (fstat(file_fd, &sb) < 0) 233 err(1, "fstat"); 234 length = sb.st_size - offset; 235 } 236 237 if (off != length) { 238 errx(1, "sendfile: off(%ju) != length(%ju)", 239 (uintmax_t)off, (uintmax_t)length); 240 } 241 242 if (header != NULL) 243 free(header); 244 } 245 246 static void 247 run_parent(void) 248 { 249 int connect_socket; 250 251 connect_socket = new_test_socket(); 252 send_test(connect_socket, 0, 0, 1); 253 close(connect_socket); 254 255 sleep(1); 256 257 connect_socket = new_test_socket(); 258 send_test(connect_socket, 0, 0, getpagesize()); 259 close(connect_socket); 260 261 sleep(1); 262 263 connect_socket = new_test_socket(); 264 send_test(connect_socket, 0, 1, 1); 265 close(connect_socket); 266 267 sleep(1); 268 269 connect_socket = new_test_socket(); 270 send_test(connect_socket, 0, 1, getpagesize()); 271 close(connect_socket); 272 273 sleep(1); 274 275 connect_socket = new_test_socket(); 276 send_test(connect_socket, 0, getpagesize(), getpagesize()); 277 close(connect_socket); 278 279 sleep(1); 280 281 connect_socket = new_test_socket(); 282 send_test(connect_socket, 0, 0, 2 * getpagesize()); 283 close(connect_socket); 284 285 sleep(1); 286 287 connect_socket = new_test_socket(); 288 send_test(connect_socket, 0, 0, 0); 289 close(connect_socket); 290 291 sleep(1); 292 293 connect_socket = new_test_socket(); 294 send_test(connect_socket, 0, getpagesize(), 0); 295 close(connect_socket); 296 297 sleep(1); 298 299 connect_socket = new_test_socket(); 300 send_test(connect_socket, 0, 2 * getpagesize(), 0); 301 close(connect_socket); 302 303 sleep(1); 304 305 connect_socket = new_test_socket(); 306 send_test(connect_socket, 0, TEST_PAGES * getpagesize(), 0); 307 close(connect_socket); 308 309 sleep(1); 310 311 (void)kill(child_pid, SIGKILL); 312 } 313 314 int 315 main(void) 316 { 317 char path[PATH_MAX], *page_buffer; 318 struct sockaddr_in sin; 319 int pagesize; 320 ssize_t len; 321 322 pagesize = getpagesize(); 323 page_buffer = malloc(TEST_PAGES * pagesize); 324 if (page_buffer == NULL) 325 err(1, "malloc"); 326 bzero(page_buffer, TEST_PAGES * pagesize); 327 328 listen_socket = socket(PF_INET, SOCK_STREAM, 0); 329 if (listen_socket < 0) 330 err(1, "socket"); 331 332 bzero(&sin, sizeof(sin)); 333 sin.sin_len = sizeof(sin); 334 sin.sin_family = AF_INET; 335 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 336 sin.sin_port = htons(TEST_PORT); 337 338 snprintf(path, PATH_MAX, "/tmp/sendfile.XXXXXXXXXXXX"); 339 file_fd = mkstemp(path); 340 (void)unlink(path); 341 342 len = write(file_fd, page_buffer, TEST_PAGES * pagesize); 343 if (len < 0) 344 err(1, "write"); 345 346 len = lseek(file_fd, 0, SEEK_SET); 347 if (len < 0) 348 err(1, "lseek"); 349 if (len != 0) 350 errx(1, "lseek: %zd", len); 351 352 if (bind(listen_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) 353 err(1, "bind"); 354 355 if (listen(listen_socket, -1) < 0) 356 err(1, "listen"); 357 358 parent_pid = getpid(); 359 child_pid = fork(); 360 if (child_pid < 0) 361 err(1, "fork"); 362 if (child_pid == 0) { 363 child_pid = getpid(); 364 run_child(); 365 } else 366 run_parent(); 367 368 return (0); 369 } 370