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