1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *   Copyright (c) Cyril Hrubis <chrubis@suse.cz> 2012
5 *
6 *   This program is free software;  you can redistribute it and/or modify
7 *   it under the terms of the GNU General Public License as published by
8 *   the Free Software Foundation; either version 2 of the License, or
9 *   (at your option) any later version.
10 *
11 *   This program is distributed in the hope that it will be useful,
12 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
14 *   the GNU General Public License for more details.
15 *
16 *   You should have received a copy of the GNU General Public License
17 *   along with this program;  if not, write to the Free Software
18 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/*
22 * Test Name: send01
23 *
24 * Test Description:
25 *  Verify that send() returns the proper errno for various failure cases
26 *
27 * HISTORY
28 *	07/2001 Ported by Wayne Boyer
29 *
30 */
31
32#include <stdio.h>
33#include <unistd.h>
34#include <errno.h>
35#include <fcntl.h>
36
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <sys/signal.h>
40#include <sys/un.h>
41
42#include <netinet/in.h>
43
44#include "test.h"
45#include "safe_macros.h"
46
47char *TCID = "send01";
48int testno;
49
50static char buf[1024], bigbuf[128 * 1024];
51static int s;
52static struct sockaddr_in sin1;
53static int sfd;			/* shared between do_child and start_server */
54
55struct test_case_t {		/* test case structure */
56	int domain;		/* PF_INET, PF_UNIX, ... */
57	int type;		/* SOCK_STREAM, SOCK_DGRAM ... */
58	int proto;		/* protocol number (usually 0 = default) */
59	void *buf;		/* send data buffer */
60	int buflen;		/* send's 3rd argument */
61	unsigned flags;		/* send's 4th argument */
62	int retval;
63	int experrno;
64	void (*setup) (void);
65	void (*cleanup) (void);
66	char *desc;
67};
68
69static void cleanup(void);
70static void do_child(void);
71static void setup(void);
72static void setup0(void);
73static void setup1(void);
74static void setup2(void);
75static void cleanup0(void);
76static void cleanup1(void);
77
78static struct test_case_t tdat[] = {
79	{.domain = PF_INET,
80	 .type = SOCK_STREAM,
81	 .proto = 0,
82	 .buf = buf,
83	 .buflen = sizeof(buf),
84	 .flags = 0,
85	 .retval = -1,
86	 .experrno = EBADF,
87	 .setup = setup0,
88	 .cleanup = cleanup0,
89	 .desc = "bad file descriptor"}
90	,
91	{.domain = 0,
92	 .type = 0,
93	 .proto = 0,
94	 .buf = buf,
95	 .buflen = sizeof(buf),
96	 .flags = 0,
97	 .retval = -1,
98	 .experrno = ENOTSOCK,
99	 .setup = setup0,
100	 .cleanup = cleanup0,
101	 .desc = "invalid socket"}
102	,
103#ifndef UCLINUX
104	/* Skip since uClinux does not implement memory protection */
105	{.domain = PF_INET,
106	 .type = SOCK_STREAM,
107	 .proto = 0,
108	 .buf = (void *)-1,
109	 .buflen = sizeof(buf),
110	 .flags = 0,
111	 .retval = -1,
112	 .experrno = EFAULT,
113	 .setup = setup1,
114	 .cleanup = cleanup1,
115	 .desc = "invalid send buffer"}
116	,
117#endif
118	{.domain = PF_INET,
119	 .type = SOCK_DGRAM,
120	 .proto = 0,
121	 .buf = bigbuf,
122	 .buflen = sizeof(bigbuf),
123	 .flags = 0,
124	 .retval = -1,
125	 .experrno = EMSGSIZE,
126	 .setup = setup1,
127	 .cleanup = cleanup1,
128	 .desc = "UDP message too big"}
129	,
130	{.domain = PF_INET,
131	 .type = SOCK_STREAM,
132	 .proto = 0,
133	 .buf = buf,
134	 .buflen = sizeof(buf),
135	 .flags = 0,
136	 .retval = -1,
137	 .experrno = EPIPE,
138	 .setup = setup2,
139	 .cleanup = cleanup1,
140	 .desc = "local endpoint shutdown"}
141	,
142#ifndef UCLINUX
143	/* Skip since uClinux does not implement memory protection */
144	{.domain = PF_INET,
145	 .type = SOCK_DGRAM,
146	 .proto = 0,
147	 .buf = buf,
148	 .buflen = sizeof(buf),
149	 .flags = MSG_OOB,
150	 .retval = -1,
151	 .experrno = EOPNOTSUPP,
152	 .setup = setup1,
153	 .cleanup = cleanup1,
154	 .desc = "invalid flags set"}
155#endif
156};
157
158int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]);
159
160#ifdef UCLINUX
161static char *argv0;
162#endif
163
164static pid_t start_server(struct sockaddr_in *sin0)
165{
166	pid_t pid;
167	socklen_t slen = sizeof(*sin0);
168
169	sin0->sin_family = AF_INET;
170	sin0->sin_port = 0; /* pick random free port */
171	sin0->sin_addr.s_addr = INADDR_ANY;
172
173	sfd = socket(PF_INET, SOCK_STREAM, 0);
174	if (sfd < 0) {
175		tst_brkm(TBROK | TERRNO, cleanup, "server socket failed");
176		return -1;
177	}
178	if (bind(sfd, (struct sockaddr *)sin0, sizeof(*sin0)) < 0) {
179		tst_brkm(TBROK | TERRNO, cleanup, "server bind failed");
180		return -1;
181	}
182	if (listen(sfd, 10) < 0) {
183		tst_brkm(TBROK | TERRNO, cleanup, "server listen failed");
184		return -1;
185	}
186	SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen);
187
188	switch ((pid = FORK_OR_VFORK())) {
189	case 0:
190#ifdef UCLINUX
191		if (self_exec(argv0, "d", sfd) < 0)
192			tst_brkm(TBROK | TERRNO, cleanup,
193				 "server self_exec failed");
194#else
195		do_child();
196#endif
197		break;
198	case -1:
199		tst_brkm(TBROK | TERRNO, cleanup, "server fork failed");
200	default:
201		close(sfd);
202		return pid;
203	}
204
205	exit(1);
206}
207
208static void do_child(void)
209{
210	fd_set afds, rfds;
211	int nfds, cc, fd;
212	struct sockaddr_in fsin;
213
214	FD_ZERO(&afds);
215	FD_SET(sfd, &afds);
216
217	nfds = sfd + 1;
218
219	/* accept connections until killed */
220	while (1) {
221		socklen_t fromlen;
222
223		memcpy(&rfds, &afds, sizeof(rfds));
224
225		if (select(nfds, &rfds, NULL, NULL, NULL) < 0)
226			if (errno != EINTR)
227				exit(1);
228		if (FD_ISSET(sfd, &rfds)) {
229			int newfd;
230
231			fromlen = sizeof(fsin);
232			newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen);
233			if (newfd >= 0) {
234				FD_SET(newfd, &afds);
235				nfds = MAX(nfds, newfd + 1);
236			}
237		}
238		for (fd = 0; fd < nfds; ++fd) {
239			if (fd != sfd && FD_ISSET(fd, &rfds)) {
240				cc = read(fd, buf, sizeof(buf));
241				if (cc == 0 || (cc < 0 && errno != EINTR)) {
242					close(fd);
243					FD_CLR(fd, &afds);
244				}
245			}
246		}
247	}
248}
249
250int main(int ac, char *av[])
251{
252	int lc;
253
254	tst_parse_opts(ac, av, NULL, NULL);
255
256#ifdef UCLINUX
257	argv0 = av[0];
258	maybe_run_child(&do_child, "d", &sfd);
259#endif
260
261	setup();
262
263	for (lc = 0; TEST_LOOPING(lc); ++lc) {
264
265		tst_count = 0;
266
267		for (testno = 0; testno < TST_TOTAL; ++testno) {
268			tdat[testno].setup();
269
270			TEST(send(s, tdat[testno].buf, tdat[testno].buflen,
271				  tdat[testno].flags));
272
273			if (TEST_RETURN != -1) {
274				tst_resm(TFAIL, "call succeeded unexpectedly");
275				continue;
276			}
277
278			if (TEST_ERRNO != tdat[testno].experrno) {
279				tst_resm(TFAIL, "%s ; returned"
280					 " %ld (expected %d), errno %d (expected"
281					 " %d)", tdat[testno].desc,
282					 TEST_RETURN, tdat[testno].retval,
283					 TEST_ERRNO, tdat[testno].experrno);
284			} else {
285				tst_resm(TPASS, "%s successful",
286					 tdat[testno].desc);
287			}
288			tdat[testno].cleanup();
289		}
290	}
291	cleanup();
292
293	tst_exit();
294}
295
296static pid_t server_pid;
297
298static void setup(void)
299{
300	TEST_PAUSE;
301
302	server_pid = start_server(&sin1);
303
304	signal(SIGPIPE, SIG_IGN);
305}
306
307static void cleanup(void)
308{
309	kill(server_pid, SIGKILL);
310
311}
312
313static void setup0(void)
314{
315	if (tdat[testno].experrno == EBADF)
316		s = 400;	/* anything not an open file */
317	else if ((s = open("/dev/null", O_WRONLY)) == -1)
318		tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed");
319}
320
321static void cleanup0(void)
322{
323	s = -1;
324}
325
326static void setup1(void)
327{
328	s = SAFE_SOCKET(cleanup, tdat[testno].domain, tdat[testno].type,
329			tdat[testno].proto);
330	SAFE_CONNECT(cleanup, s, (const struct sockaddr *)&sin1, sizeof(sin1));
331}
332
333static void cleanup1(void)
334{
335	close(s);
336	s = -1;
337}
338
339static void setup2(void)
340{
341	setup1();
342
343	if (shutdown(s, 1) < 0)
344		tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed connect "
345			 "test %d", testno);
346}
347