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