1/*-
2 * Copyright (c) 2009 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * This software was developed at the University of Cambridge Computer
6 * Laboratory with support from a grant from Google, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/socket.h>
34#include <sys/wait.h>
35#include <sys/un.h>
36
37#include <err.h>
38#include <errno.h>
39#include <limits.h>
40#include <stdio.h>
41#include <signal.h>
42#include <string.h>
43#include <unistd.h>
44
45#define	min(x, y)	(x < y ? x : y)
46
47#define	BUFLEN	32768
48
49#define	SEQPACKET_RCVBUF	(131072-16)
50#define	SEQPACKET_SNDBUF	(131072-16)
51
52#define	FAILERR(str)		err(-1, "%s: %s", __func__, str)
53#define	FAILNERR(str, n)	err(-1, "%s %d: %s", __func__, n, str)
54#define	FAILNMERR(str, n, m)	err(-1, "%s %d %d: %s", __func__, n, m, str)
55#define	FAILERRX(str)		errx(-1, "%s: %s", __func__, str)
56#define	FAILNERRX(str, n)	errx(-1, "%s %d: %s", __func__, n, str)
57#define	FAILNMERRX(str, n, m)	errx(-1, "%s %d %d: %s", __func__, n, m, str)
58
59static int ann = 0;
60
61#define	ANN()		(ann ? warnx("%s: start", __func__) : 0)
62#define	ANNN(n)		(ann ? warnx("%s %d: start", __func__, (n)) : 0)
63#define	ANNNM(n, m)	(ann ? warnx("%s %d %d: start", __func__, (n), (m)) : 0)
64
65#define	OK()		warnx("%s: ok", __func__)
66#define	OKN(n)		warnx("%s %d: ok", __func__, (n))
67#define	OKNM(n, m)	warnx("%s %d %d: ok", __func__, (n), (m))
68
69#ifdef SO_NOSIGPIPE
70#define	NEW_SOCKET(s) do {						\
71	int i;								\
72									\
73	(s) = socket(PF_LOCAL, SOCK_SEQPACKET, 0);			\
74	if ((s) < 0)							\
75		FAILERR("socket");					\
76									\
77	i = 1;								\
78	if (setsockopt((s), SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) \
79		FAILERR("setsockopt SO_NOSIGPIPE");			\
80									\
81	i = SEQPACKET_RCVBUF;						\
82	if (setsockopt((s), SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)	\
83		FAILERR("setsockopt SO_RCVBUF");			\
84									\
85	i = SEQPACKET_SNDBUF;						\
86	if (setsockopt((s), SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0)	\
87		FAILERR("setsockopt SO_SNDBUF");			\
88} while (0)
89#else
90#define	NEW_SOCKET(s) do {						\
91	int i;								\
92									\
93	(s) = socket(PF_LOCAL, SOCK_SEQPACKET, 0);			\
94	if ((s) < 0)							\
95		FAILERR("socket");					\
96									\
97	i = SEQPACKET_RCVBUF;						\
98	if (setsockopt((s), SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)	\
99		FAILERR("setsockopt SO_RCVBUF");			\
100									\
101	i = SEQPACKET_SNDBUF;						\
102	if (setsockopt((s), SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0)	\
103		FAILERR("setsockopt SO_SNDBUF");			\
104} while (0)
105#endif
106
107static void
108server(int s_listen)
109{
110	char buffer[BUFLEN];
111	ssize_t ssize_recv, ssize_send;
112	socklen_t socklen;
113	int i, s_accept;
114
115	while (1) {
116		s_accept = accept(s_listen, NULL, 0);
117		if (s_accept >= 0) {
118			i = SEQPACKET_RCVBUF;
119			if (setsockopt(s_accept, SOL_SOCKET, SO_RCVBUF, &i,
120			    sizeof(i)) < 0) {
121				warn("server: setsockopt SO_RCVBUF");
122				close(s_accept);
123				continue;
124			}
125
126			if (getsockopt(s_accept, SOL_SOCKET, SO_RCVBUF, &i,
127			    &socklen) < 0) {
128				warn("server: getsockopt SO_RCVBUF");
129				close(s_accept);
130				continue;
131			}
132			if (i != SEQPACKET_RCVBUF) {
133				warnx("server: getsockopt SO_RCVBUF wrong %d",
134				    i);
135				close(s_accept);
136				continue;
137			}
138
139			socklen = sizeof(i);
140			if (getsockopt(s_accept, SOL_SOCKET, SO_SNDBUF, &i,
141			    &socklen) < 0) {
142				warn("server: getsockopt SO_SNDBUF");
143				close(s_accept);
144				continue;
145			}
146			if (i != SEQPACKET_SNDBUF) {
147				warnx("server: getsockopt SO_SNDBUF wrong %d",
148				    i);
149				close(s_accept);
150				continue;
151			}
152
153			do {
154				ssize_recv = recv(s_accept, buffer,
155				    sizeof(buffer), 0);
156				if (ssize_recv == 0)
157					break;
158				if (ssize_recv < 0) {
159					warn("server: recv");
160					break;
161				}
162				ssize_send = send(s_accept, buffer,
163				    ssize_recv, 0);
164				if (ssize_send == 0)
165					break;
166				if (ssize_send < 0) {
167					warn("server: send");
168					break;
169				}
170				if (ssize_send != ssize_recv)
171					warnx("server: recv %d sent %d",
172					    ssize_recv, ssize_send);
173			} while (1);
174			close(s_accept);
175		} else
176			warn("server: accept");
177	}
178}
179
180static void
181test_connect(struct sockaddr_un *sun)
182{
183	int s;
184
185	ANN();
186	NEW_SOCKET(s);
187	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
188		FAILERR("connect");
189	(void)close(s);
190	OK();
191}
192
193static void
194test_connect_send(struct sockaddr_un *sun)
195{
196	ssize_t ssize;
197	char ch;
198	int s;
199
200	ANN();
201	NEW_SOCKET(s);
202	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
203		FAILERR("connect");
204	ssize = send(s, &ch, sizeof(ch), 0);
205	if (ssize < 0)
206		FAILERR("send");
207	if (ssize != sizeof(ch))
208		FAILERRX("send wrong size");
209	(void)close(s);
210	OK();
211}
212
213static void
214test_connect_shutdown_send(struct sockaddr_un *sun)
215{
216	ssize_t ssize;
217	char ch;
218	int s;
219
220	ANN();
221	NEW_SOCKET(s);
222	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
223		FAILERR("connect");
224	if (shutdown(s, SHUT_RDWR) < 0)
225		FAILERR("shutdown SHUT_RDWR");
226	ssize = send(s, &ch, sizeof(ch), 0);
227	if (ssize >= 0)
228		FAILERRX("send");
229	if (errno != EPIPE)
230		FAILERR("send unexpected error");
231	(void)close(s);
232	OK();
233}
234
235static void
236test_connect_send_recv(struct sockaddr_un *sun, size_t size)
237{
238	char buf[size + 4];	/* Detect extra bytes. */
239	size_t truncsize;
240	ssize_t ssize;
241	int s;
242
243	ANNN(size);
244	NEW_SOCKET(s);
245	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
246		FAILNERR("connect", size);
247	ssize = send(s, buf, size, 0);
248	if (ssize < 0 && size >= SEQPACKET_RCVBUF)
249		goto out;
250	if (ssize < 0)
251		FAILNERR("send", size);
252	if (ssize == 0)
253		FAILNERR("send eof", size);
254	if (ssize != size)
255		FAILNERRX("send size", size);
256
257	truncsize = min(size, BUFLEN);
258	ssize = recv(s, buf, sizeof(buf), 0);
259	if (ssize < 0)
260		FAILNERR("recv", size);
261	if (ssize == 0)
262		FAILNERRX("recv eof", size);
263	if (ssize < truncsize)
264		FAILNERRX("recv too few bytes", size);
265	if (ssize > truncsize)
266		FAILNERRX("recv too many bytes", size);
267out:
268	(void)close(s);
269	OKN(size);
270}
271
272static void
273test_connect_send_recv_count(struct sockaddr_un *sun, int count, size_t size)
274{
275	char buf[size + 4];	/* Detect extra bytes and coalescing. */
276	size_t truncsize;
277	ssize_t ssize;
278	int i, s;
279
280	ANNNM(size, count);
281	NEW_SOCKET(s);
282	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
283		FAILNMERR("connect", size, count);
284	for (i = 0; i < count; i++) {
285		usleep(5000);
286		ssize = send(s, buf, size, 0);
287		if (ssize < 0 && size >= SEQPACKET_RCVBUF)
288			goto out;
289		if (ssize < 0)
290			FAILNMERR("send", size, count);
291		if (ssize == 0)
292			FAILNMERRX("send eof", size, count);
293		if (ssize != size)
294			FAILNMERRX("send size", size, count);
295	}
296
297	truncsize = min(size, BUFLEN);
298	for (i = 0; i < count; i++) {
299		ssize = recv(s, buf, sizeof(buf), 0);
300		if (ssize < 0)
301			FAILNMERR("recv", size, count);
302		if (ssize == 0)
303			FAILNMERRX("recv eof", size, count);
304		if (ssize < truncsize)
305			FAILNMERRX("recv too few bytes", size, count);
306		if (ssize > truncsize)
307			FAILNMERRX("recv too many bytes", size, count);
308	}
309out:
310	(void)close(s);
311	OKNM(size, count);
312}
313
314static void
315test_sendto(struct sockaddr_un *sun)
316{
317	ssize_t ssize;
318	char ch;
319	int s;
320
321	ANN();
322	NEW_SOCKET(s);
323	ssize = sendto(s, &ch, sizeof(ch), 0, (struct sockaddr *)sun,
324	    sizeof(*sun));
325	if (ssize < 0)
326		FAILERR("sendto");
327	(void)close(s);
328	OK();
329}
330
331static void
332client(struct sockaddr_un *sun)
333{
334	size_t sizes[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
335	    4096, 8192, 16384, 32768, 65536 /*, 131072 */};
336	int c, i;
337
338	test_connect(sun);
339	test_connect_send(sun);
340	test_connect_shutdown_send(sun);
341
342	/*
343	 * Try a range of sizes and packet counts.
344	 */
345	for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
346		test_connect_send_recv(sun, sizes[i]);
347	for (c = 1; c <= 8; c++) {
348		for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
349			test_connect_send_recv_count(sun, c, sizes[i]);
350	}
351	test_sendto(sun);
352	printf("client done\n");
353}
354
355int
356main(int argc, char *argv[])
357{
358	struct sockaddr_un sun;
359	char path[PATH_MAX];
360	pid_t pid_client, pid_server;
361	int i, s_listen;
362
363	snprintf(path, sizeof(path), "/tmp/lds_exercise.XXXXXXXXX");
364	if (mktemp(path) == NULL)
365		FAILERR("mktemp");
366
367	s_listen = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
368	if (s_listen < 0) {
369		(void)unlink(path);
370		FAILERR("socket");
371	}
372
373	i = SEQPACKET_RCVBUF;
374	if (setsockopt(s_listen, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0) {
375		(void)unlink(path);
376		FAILERR("setsockopt SO_RCVBUF");
377	}
378
379	i = SEQPACKET_SNDBUF;
380	if (setsockopt(s_listen, SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0) {
381		(void)unlink(path);
382		FAILERR("setsockopt SO_SNDBUF");
383	}
384
385	i = 1;
386	if (setsockopt(s_listen, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i))
387	    < 0) {
388		(void)unlink(path);
389		FAILERR("setsockopt SO_NOSIGPIPE");
390	}
391
392	bzero(&sun, sizeof(sun));
393	sun.sun_len = sizeof(sun);
394	sun.sun_family = AF_LOCAL;
395	strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
396
397	if (bind(s_listen, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
398		(void)unlink(path);
399		FAILERR("bind");
400	}
401
402	if (listen(s_listen, -1) < 0) {
403		(void)unlink(path);
404		FAILERR("listen");
405	}
406
407	pid_server = fork();
408	if (pid_server < 0) {
409		(void)unlink(path);
410		FAILERR("fork");
411	}
412	if (pid_server == 0) {
413		server(s_listen);
414		return (0);
415	}
416
417	pid_client = fork();
418	if (pid_client < 0) {
419		(void)kill(pid_server, SIGKILL);
420		(void)unlink(path);
421		FAILERR("fork");
422	}
423	if (pid_client == 0) {
424		client(&sun);
425		return (0);
426	}
427
428	/*
429	 * When the client is done, kill the server and clean up.
430	 */
431	(void)waitpid(pid_client, NULL, 0);
432	(void)kill(pid_server, SIGKILL);
433	(void)unlink(path);
434	return (0);
435}
436