1197781Srwatson/*-
2197781Srwatson * Copyright (c) 2009 Robert N. M. Watson
3197781Srwatson * All rights reserved.
4197781Srwatson *
5197781Srwatson * This software was developed at the University of Cambridge Computer
6197781Srwatson * Laboratory with support from a grant from Google, Inc.
7197781Srwatson *
8197781Srwatson * Redistribution and use in source and binary forms, with or without
9197781Srwatson * modification, are permitted provided that the following conditions
10197781Srwatson * are met:
11197781Srwatson * 1. Redistributions of source code must retain the above copyright
12197781Srwatson *    notice, this list of conditions and the following disclaimer.
13197781Srwatson * 2. Redistributions in binary form must reproduce the above copyright
14197781Srwatson *    notice, this list of conditions and the following disclaimer in the
15197781Srwatson *    documentation and/or other materials provided with the distribution.
16197781Srwatson *
17197781Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18197781Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19197781Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20197781Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21197781Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22197781Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23197781Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24197781Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25197781Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26197781Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27197781Srwatson * SUCH DAMAGE.
28197781Srwatson */
29197781Srwatson
30197781Srwatson#include <sys/cdefs.h>
31197781Srwatson__FBSDID("$FreeBSD$");
32197781Srwatson
33197781Srwatson#include <sys/socket.h>
34197781Srwatson#include <sys/wait.h>
35197781Srwatson#include <sys/un.h>
36197781Srwatson
37197781Srwatson#include <err.h>
38197781Srwatson#include <errno.h>
39197781Srwatson#include <limits.h>
40197781Srwatson#include <stdio.h>
41197781Srwatson#include <signal.h>
42197781Srwatson#include <string.h>
43197781Srwatson#include <unistd.h>
44197781Srwatson
45197781Srwatson#define	min(x, y)	(x < y ? x : y)
46197781Srwatson
47197781Srwatson#define	BUFLEN	32768
48197781Srwatson
49197781Srwatson#define	SEQPACKET_RCVBUF	(131072-16)
50197781Srwatson#define	SEQPACKET_SNDBUF	(131072-16)
51197781Srwatson
52197781Srwatson#define	FAILERR(str)		err(-1, "%s: %s", __func__, str)
53197781Srwatson#define	FAILNERR(str, n)	err(-1, "%s %d: %s", __func__, n, str)
54197781Srwatson#define	FAILNMERR(str, n, m)	err(-1, "%s %d %d: %s", __func__, n, m, str)
55197781Srwatson#define	FAILERRX(str)		errx(-1, "%s: %s", __func__, str)
56197781Srwatson#define	FAILNERRX(str, n)	errx(-1, "%s %d: %s", __func__, n, str)
57197781Srwatson#define	FAILNMERRX(str, n, m)	errx(-1, "%s %d %d: %s", __func__, n, m, str)
58197781Srwatson
59197781Srwatsonstatic int ann = 0;
60197781Srwatson
61197781Srwatson#define	ANN()		(ann ? warnx("%s: start", __func__) : 0)
62197781Srwatson#define	ANNN(n)		(ann ? warnx("%s %d: start", __func__, (n)) : 0)
63197781Srwatson#define	ANNNM(n, m)	(ann ? warnx("%s %d %d: start", __func__, (n), (m)) : 0)
64197781Srwatson
65197781Srwatson#define	OK()		warnx("%s: ok", __func__)
66197781Srwatson#define	OKN(n)		warnx("%s %d: ok", __func__, (n))
67197781Srwatson#define	OKNM(n, m)	warnx("%s %d %d: ok", __func__, (n), (m))
68197781Srwatson
69197781Srwatson#ifdef SO_NOSIGPIPE
70197781Srwatson#define	NEW_SOCKET(s) do {						\
71197781Srwatson	int i;								\
72197781Srwatson									\
73197781Srwatson	(s) = socket(PF_LOCAL, SOCK_SEQPACKET, 0);			\
74197781Srwatson	if ((s) < 0)							\
75197781Srwatson		FAILERR("socket");					\
76197781Srwatson									\
77197781Srwatson	i = 1;								\
78197781Srwatson	if (setsockopt((s), SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i)) < 0) \
79197781Srwatson		FAILERR("setsockopt SO_NOSIGPIPE");			\
80197781Srwatson									\
81197781Srwatson	i = SEQPACKET_RCVBUF;						\
82197781Srwatson	if (setsockopt((s), SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)	\
83197781Srwatson		FAILERR("setsockopt SO_RCVBUF");			\
84197781Srwatson									\
85197781Srwatson	i = SEQPACKET_SNDBUF;						\
86197781Srwatson	if (setsockopt((s), SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0)	\
87197781Srwatson		FAILERR("setsockopt SO_SNDBUF");			\
88197781Srwatson} while (0)
89197781Srwatson#else
90197781Srwatson#define	NEW_SOCKET(s) do {						\
91197781Srwatson	int i;								\
92197781Srwatson									\
93197781Srwatson	(s) = socket(PF_LOCAL, SOCK_SEQPACKET, 0);			\
94197781Srwatson	if ((s) < 0)							\
95197781Srwatson		FAILERR("socket");					\
96197781Srwatson									\
97197781Srwatson	i = SEQPACKET_RCVBUF;						\
98197781Srwatson	if (setsockopt((s), SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)	\
99197781Srwatson		FAILERR("setsockopt SO_RCVBUF");			\
100197781Srwatson									\
101197781Srwatson	i = SEQPACKET_SNDBUF;						\
102197781Srwatson	if (setsockopt((s), SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0)	\
103197781Srwatson		FAILERR("setsockopt SO_SNDBUF");			\
104197781Srwatson} while (0)
105197781Srwatson#endif
106197781Srwatson
107197781Srwatsonstatic void
108197781Srwatsonserver(int s_listen)
109197781Srwatson{
110197781Srwatson	char buffer[BUFLEN];
111197781Srwatson	ssize_t ssize_recv, ssize_send;
112197781Srwatson	socklen_t socklen;
113197781Srwatson	int i, s_accept;
114197781Srwatson
115197781Srwatson	while (1) {
116197781Srwatson		s_accept = accept(s_listen, NULL, 0);
117197781Srwatson		if (s_accept >= 0) {
118197781Srwatson			i = SEQPACKET_RCVBUF;
119197781Srwatson			if (setsockopt(s_accept, SOL_SOCKET, SO_RCVBUF, &i,
120197781Srwatson			    sizeof(i)) < 0) {
121197781Srwatson				warn("server: setsockopt SO_RCVBUF");
122197781Srwatson				close(s_accept);
123197781Srwatson				continue;
124197781Srwatson			}
125197781Srwatson
126197781Srwatson			if (getsockopt(s_accept, SOL_SOCKET, SO_RCVBUF, &i,
127197781Srwatson			    &socklen) < 0) {
128197781Srwatson				warn("server: getsockopt SO_RCVBUF");
129197781Srwatson				close(s_accept);
130197781Srwatson				continue;
131197781Srwatson			}
132197781Srwatson			if (i != SEQPACKET_RCVBUF) {
133197781Srwatson				warnx("server: getsockopt SO_RCVBUF wrong %d",
134197781Srwatson				    i);
135197781Srwatson				close(s_accept);
136197781Srwatson				continue;
137197781Srwatson			}
138197781Srwatson
139197781Srwatson			socklen = sizeof(i);
140197781Srwatson			if (getsockopt(s_accept, SOL_SOCKET, SO_SNDBUF, &i,
141197781Srwatson			    &socklen) < 0) {
142197781Srwatson				warn("server: getsockopt SO_SNDBUF");
143197781Srwatson				close(s_accept);
144197781Srwatson				continue;
145197781Srwatson			}
146197781Srwatson			if (i != SEQPACKET_SNDBUF) {
147197781Srwatson				warnx("server: getsockopt SO_SNDBUF wrong %d",
148197781Srwatson				    i);
149197781Srwatson				close(s_accept);
150197781Srwatson				continue;
151197781Srwatson			}
152197781Srwatson
153197781Srwatson			do {
154197781Srwatson				ssize_recv = recv(s_accept, buffer,
155197781Srwatson				    sizeof(buffer), 0);
156197781Srwatson				if (ssize_recv == 0)
157197781Srwatson					break;
158197781Srwatson				if (ssize_recv < 0) {
159197781Srwatson					warn("server: recv");
160197781Srwatson					break;
161197781Srwatson				}
162197781Srwatson				ssize_send = send(s_accept, buffer,
163197781Srwatson				    ssize_recv, 0);
164197781Srwatson				if (ssize_send == 0)
165197781Srwatson					break;
166197781Srwatson				if (ssize_send < 0) {
167197781Srwatson					warn("server: send");
168197781Srwatson					break;
169197781Srwatson				}
170197781Srwatson				if (ssize_send != ssize_recv)
171197781Srwatson					warnx("server: recv %d sent %d",
172197781Srwatson					    ssize_recv, ssize_send);
173197781Srwatson			} while (1);
174197781Srwatson			close(s_accept);
175197781Srwatson		} else
176197781Srwatson			warn("server: accept");
177197781Srwatson	}
178197781Srwatson}
179197781Srwatson
180197781Srwatsonstatic void
181197781Srwatsontest_connect(struct sockaddr_un *sun)
182197781Srwatson{
183197781Srwatson	int s;
184197781Srwatson
185197781Srwatson	ANN();
186197781Srwatson	NEW_SOCKET(s);
187197781Srwatson	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
188197781Srwatson		FAILERR("connect");
189197781Srwatson	(void)close(s);
190197781Srwatson	OK();
191197781Srwatson}
192197781Srwatson
193197781Srwatsonstatic void
194197781Srwatsontest_connect_send(struct sockaddr_un *sun)
195197781Srwatson{
196197781Srwatson	ssize_t ssize;
197197781Srwatson	char ch;
198197781Srwatson	int s;
199197781Srwatson
200197781Srwatson	ANN();
201197781Srwatson	NEW_SOCKET(s);
202197781Srwatson	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
203197781Srwatson		FAILERR("connect");
204197781Srwatson	ssize = send(s, &ch, sizeof(ch), 0);
205197781Srwatson	if (ssize < 0)
206197781Srwatson		FAILERR("send");
207197781Srwatson	if (ssize != sizeof(ch))
208197781Srwatson		FAILERRX("send wrong size");
209197781Srwatson	(void)close(s);
210197781Srwatson	OK();
211197781Srwatson}
212197781Srwatson
213197781Srwatsonstatic void
214197781Srwatsontest_connect_shutdown_send(struct sockaddr_un *sun)
215197781Srwatson{
216197781Srwatson	ssize_t ssize;
217197781Srwatson	char ch;
218197781Srwatson	int s;
219197781Srwatson
220197781Srwatson	ANN();
221197781Srwatson	NEW_SOCKET(s);
222197781Srwatson	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
223197781Srwatson		FAILERR("connect");
224197781Srwatson	if (shutdown(s, SHUT_RDWR) < 0)
225197781Srwatson		FAILERR("shutdown SHUT_RDWR");
226197781Srwatson	ssize = send(s, &ch, sizeof(ch), 0);
227197781Srwatson	if (ssize >= 0)
228197781Srwatson		FAILERRX("send");
229197781Srwatson	if (errno != EPIPE)
230197781Srwatson		FAILERR("send unexpected error");
231197781Srwatson	(void)close(s);
232197781Srwatson	OK();
233197781Srwatson}
234197781Srwatson
235197781Srwatsonstatic void
236197781Srwatsontest_connect_send_recv(struct sockaddr_un *sun, size_t size)
237197781Srwatson{
238197781Srwatson	char buf[size + 4];	/* Detect extra bytes. */
239197781Srwatson	size_t truncsize;
240197781Srwatson	ssize_t ssize;
241197781Srwatson	int s;
242197781Srwatson
243197781Srwatson	ANNN(size);
244197781Srwatson	NEW_SOCKET(s);
245197781Srwatson	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
246197781Srwatson		FAILNERR("connect", size);
247197781Srwatson	ssize = send(s, buf, size, 0);
248197781Srwatson	if (ssize < 0 && size >= SEQPACKET_RCVBUF)
249197781Srwatson		goto out;
250197781Srwatson	if (ssize < 0)
251197781Srwatson		FAILNERR("send", size);
252197781Srwatson	if (ssize == 0)
253197781Srwatson		FAILNERR("send eof", size);
254197781Srwatson	if (ssize != size)
255197781Srwatson		FAILNERRX("send size", size);
256197781Srwatson
257197781Srwatson	truncsize = min(size, BUFLEN);
258197781Srwatson	ssize = recv(s, buf, sizeof(buf), 0);
259197781Srwatson	if (ssize < 0)
260197781Srwatson		FAILNERR("recv", size);
261197781Srwatson	if (ssize == 0)
262197781Srwatson		FAILNERRX("recv eof", size);
263197781Srwatson	if (ssize < truncsize)
264197781Srwatson		FAILNERRX("recv too few bytes", size);
265197781Srwatson	if (ssize > truncsize)
266197781Srwatson		FAILNERRX("recv too many bytes", size);
267197781Srwatsonout:
268197781Srwatson	(void)close(s);
269197781Srwatson	OKN(size);
270197781Srwatson}
271197781Srwatson
272197781Srwatsonstatic void
273197781Srwatsontest_connect_send_recv_count(struct sockaddr_un *sun, int count, size_t size)
274197781Srwatson{
275197781Srwatson	char buf[size + 4];	/* Detect extra bytes and coalescing. */
276197781Srwatson	size_t truncsize;
277197781Srwatson	ssize_t ssize;
278197781Srwatson	int i, s;
279197781Srwatson
280197781Srwatson	ANNNM(size, count);
281197781Srwatson	NEW_SOCKET(s);
282197781Srwatson	if (connect(s, (struct sockaddr *)sun, sizeof(*sun)) < 0)
283197781Srwatson		FAILNMERR("connect", size, count);
284197781Srwatson	for (i = 0; i < count; i++) {
285197781Srwatson		usleep(5000);
286197781Srwatson		ssize = send(s, buf, size, 0);
287197781Srwatson		if (ssize < 0 && size >= SEQPACKET_RCVBUF)
288197781Srwatson			goto out;
289197781Srwatson		if (ssize < 0)
290197781Srwatson			FAILNMERR("send", size, count);
291197781Srwatson		if (ssize == 0)
292197781Srwatson			FAILNMERRX("send eof", size, count);
293197781Srwatson		if (ssize != size)
294197781Srwatson			FAILNMERRX("send size", size, count);
295197781Srwatson	}
296197781Srwatson
297197781Srwatson	truncsize = min(size, BUFLEN);
298197781Srwatson	for (i = 0; i < count; i++) {
299197781Srwatson		ssize = recv(s, buf, sizeof(buf), 0);
300197781Srwatson		if (ssize < 0)
301197781Srwatson			FAILNMERR("recv", size, count);
302197781Srwatson		if (ssize == 0)
303197781Srwatson			FAILNMERRX("recv eof", size, count);
304197781Srwatson		if (ssize < truncsize)
305197781Srwatson			FAILNMERRX("recv too few bytes", size, count);
306197781Srwatson		if (ssize > truncsize)
307197781Srwatson			FAILNMERRX("recv too many bytes", size, count);
308197781Srwatson	}
309197781Srwatsonout:
310197781Srwatson	(void)close(s);
311197781Srwatson	OKNM(size, count);
312197781Srwatson}
313197781Srwatson
314197781Srwatsonstatic void
315197781Srwatsontest_sendto(struct sockaddr_un *sun)
316197781Srwatson{
317197781Srwatson	ssize_t ssize;
318197781Srwatson	char ch;
319197781Srwatson	int s;
320197781Srwatson
321197781Srwatson	ANN();
322197781Srwatson	NEW_SOCKET(s);
323197781Srwatson	ssize = sendto(s, &ch, sizeof(ch), 0, (struct sockaddr *)sun,
324197781Srwatson	    sizeof(*sun));
325197781Srwatson	if (ssize < 0)
326197781Srwatson		FAILERR("sendto");
327197781Srwatson	(void)close(s);
328197781Srwatson	OK();
329197781Srwatson}
330197781Srwatson
331197781Srwatsonstatic void
332197781Srwatsonclient(struct sockaddr_un *sun)
333197781Srwatson{
334197781Srwatson	size_t sizes[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
335197781Srwatson	    4096, 8192, 16384, 32768, 65536 /*, 131072 */};
336197781Srwatson	int c, i;
337197781Srwatson
338197781Srwatson	test_connect(sun);
339197781Srwatson	test_connect_send(sun);
340197781Srwatson	test_connect_shutdown_send(sun);
341197781Srwatson
342197781Srwatson	/*
343197781Srwatson	 * Try a range of sizes and packet counts.
344197781Srwatson	 */
345197781Srwatson	for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
346197781Srwatson		test_connect_send_recv(sun, sizes[i]);
347197781Srwatson	for (c = 1; c <= 8; c++) {
348197781Srwatson		for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
349197781Srwatson			test_connect_send_recv_count(sun, c, sizes[i]);
350197781Srwatson	}
351197781Srwatson	test_sendto(sun);
352197781Srwatson	printf("client done\n");
353197781Srwatson}
354197781Srwatson
355197781Srwatsonint
356197781Srwatsonmain(int argc, char *argv[])
357197781Srwatson{
358197781Srwatson	struct sockaddr_un sun;
359197781Srwatson	char path[PATH_MAX];
360197781Srwatson	pid_t pid_client, pid_server;
361197781Srwatson	int i, s_listen;
362197781Srwatson
363197781Srwatson	snprintf(path, sizeof(path), "/tmp/lds_exercise.XXXXXXXXX");
364197781Srwatson	if (mktemp(path) == NULL)
365197781Srwatson		FAILERR("mktemp");
366197781Srwatson
367197781Srwatson	s_listen = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
368197781Srwatson	if (s_listen < 0) {
369197781Srwatson		(void)unlink(path);
370197781Srwatson		FAILERR("socket");
371197781Srwatson	}
372197781Srwatson
373197781Srwatson	i = SEQPACKET_RCVBUF;
374197781Srwatson	if (setsockopt(s_listen, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0) {
375197781Srwatson		(void)unlink(path);
376197781Srwatson		FAILERR("setsockopt SO_RCVBUF");
377197781Srwatson	}
378197781Srwatson
379197781Srwatson	i = SEQPACKET_SNDBUF;
380197781Srwatson	if (setsockopt(s_listen, SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0) {
381197781Srwatson		(void)unlink(path);
382197781Srwatson		FAILERR("setsockopt SO_SNDBUF");
383197781Srwatson	}
384197781Srwatson
385197781Srwatson	i = 1;
386197781Srwatson	if (setsockopt(s_listen, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i))
387197781Srwatson	    < 0) {
388197781Srwatson		(void)unlink(path);
389197781Srwatson		FAILERR("setsockopt SO_NOSIGPIPE");
390197781Srwatson	}
391197781Srwatson
392197781Srwatson	bzero(&sun, sizeof(sun));
393197781Srwatson	sun.sun_len = sizeof(sun);
394197781Srwatson	sun.sun_family = AF_LOCAL;
395197781Srwatson	strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
396197781Srwatson
397197781Srwatson	if (bind(s_listen, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
398197781Srwatson		(void)unlink(path);
399197781Srwatson		FAILERR("bind");
400197781Srwatson	}
401197781Srwatson
402197781Srwatson	if (listen(s_listen, -1) < 0) {
403197781Srwatson		(void)unlink(path);
404197781Srwatson		FAILERR("listen");
405197781Srwatson	}
406197781Srwatson
407197781Srwatson	pid_server = fork();
408197781Srwatson	if (pid_server < 0) {
409197781Srwatson		(void)unlink(path);
410197781Srwatson		FAILERR("fork");
411197781Srwatson	}
412197781Srwatson	if (pid_server == 0) {
413197781Srwatson		server(s_listen);
414197781Srwatson		return (0);
415197781Srwatson	}
416197781Srwatson
417197781Srwatson	pid_client = fork();
418197781Srwatson	if (pid_client < 0) {
419197781Srwatson		(void)kill(pid_server, SIGKILL);
420197781Srwatson		(void)unlink(path);
421197781Srwatson		FAILERR("fork");
422197781Srwatson	}
423197781Srwatson	if (pid_client == 0) {
424197781Srwatson		client(&sun);
425197781Srwatson		return (0);
426197781Srwatson	}
427197781Srwatson
428197781Srwatson	/*
429197781Srwatson	 * When the client is done, kill the server and clean up.
430197781Srwatson	 */
431197781Srwatson	(void)waitpid(pid_client, NULL, 0);
432197781Srwatson	(void)kill(pid_server, SIGKILL);
433197781Srwatson	(void)unlink(path);
434197781Srwatson	return (0);
435197781Srwatson}
436