recvmsg01.c revision d7aa17621d042bffa2bee6447b422ca53877dfa7
1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 * Test Name: recvmsg01
22 *
23 * Test Description:
24 *  Verify that recvmsg() returns the proper errno for various failure cases
25 *
26 * Usage:  <for command-line>
27 *  recvmsg01 [-c n] [-e] [-i n] [-I x] [-P x] [-t]
28 *     where,  -c n : Run n copies concurrently.
29 *             -e   : Turn on errno logging.
30 *	       -i n : Execute test n times.
31 *	       -I x : Execute test for x seconds.
32 *	       -P x : Pause for x seconds between iterations.
33 *	       -t   : Turn on syscall timing.
34 *
35 * HISTORY
36 *	07/2001 Ported by Wayne Boyer
37 *
38 * RESTRICTIONS:
39 *  None.
40 *
41 */
42
43#include <stdio.h>
44#include <unistd.h>
45#include <string.h>
46#include <errno.h>
47#include <fcntl.h>
48
49#include <sys/types.h>
50#include <sys/socket.h>
51#include <sys/signal.h>
52#include <sys/uio.h>
53#include <sys/un.h>
54#include <sys/file.h>
55
56#include <netinet/in.h>
57
58#include "test.h"
59#include "usctest.h"
60#include "msg_common.h"
61
62char *TCID = "recvmsg01";
63int testno;
64
65char buf[1024], cbuf[1024];
66int s;				/* socket descriptor */
67int passed_fd = -1;		/* rights-passing test descriptor */
68struct sockaddr_in sin1, from;
69struct sockaddr_un sun1;
70struct msghdr msgdat;
71struct cmsghdr *control = 0;
72int controllen = 0;
73struct iovec iov[1];
74static int sfd;			/* shared between do_child and start_server */
75static int ufd;			/* shared between do_child and start_server */
76
77void setup(void);
78void setup0(void);
79void setup1(void);
80void setup2(void);
81void setup3(void);
82void setup4(void);
83void cleanup(void);
84void cleanup0(void);
85void cleanup1(void);
86void cleanup2(void);
87void do_child(void);
88
89void sender(int);
90pid_t start_server(struct sockaddr_in *, struct sockaddr_un *);
91
92struct test_case_t {		/* test case structure */
93	int domain;		/* PF_INET, PF_UNIX, ... */
94	int type;		/* SOCK_STREAM, SOCK_DGRAM ... */
95	int proto;		/* protocol number (usually 0 = default) */
96	struct iovec *iov;
97	int iovcnt;
98	void *buf;		/* recv data buffer */
99	int buflen;		/* recv buffer length */
100	struct msghdr *msg;
101	unsigned flags;
102	struct sockaddr *from;	/* from address */
103	int fromlen;		/* from address value/result buffer length */
104	int retval;		/* syscall return value */
105	int experrno;		/* expected errno */
106	void (*setup) (void);
107	void (*cleanup) (void);
108	char *desc;
109} tdat[] = {
110/* 1 */
111	{
112	PF_INET, SOCK_STREAM, 0, iov, 1, buf, sizeof(buf), &msgdat, 0,
113		    (struct sockaddr *)&from, sizeof(from),
114		    -1, EBADF, setup0, cleanup0, "bad file descriptor"}
115	,
116/* 2 */
117	{
118	0, 0, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0,
119		    (struct sockaddr *)&from, sizeof(from),
120		    -1, ENOTSOCK, setup0, cleanup0, "invalid socket"}
121	,
122/* 3 */
123	{
124	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
125		    &msgdat, 0, (struct sockaddr *)-1, sizeof(from), 0,
126		    ENOTSOCK, setup1, cleanup1, "invalid socket buffer"}
127	,
128/* 4 */
129	{
130	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
131		    &msgdat, -1, (struct sockaddr *)&from, -1, -1,
132		    EINVAL, setup1, cleanup1, "invalid socket length"},
133/* 5 */
134	{
135	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)-1, sizeof(buf),
136		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
137		    -1, EFAULT, setup1, cleanup1, "invalid recv buffer"}
138	,
139/* 6 */
140	{
141	PF_INET, SOCK_STREAM, 0, 0, 1, (void *)buf, sizeof(buf),
142		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
143		    -1, EFAULT, setup1, cleanup1, "invalid iovec buffer"}
144	,
145/* 7 */
146	{
147	PF_INET, SOCK_STREAM, 0, iov, -1, (void *)buf, sizeof(buf),
148		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
149		    -1, EMSGSIZE, setup1, cleanup1, "invalid iovec count"}
150	,
151/* 8 */
152	{
153	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
154		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
155		    0, 0, setup2, cleanup2, "rights reception"}
156	,
157/* 9 */
158	{
159	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
160		    &msgdat, MSG_OOB, (struct sockaddr *)&from,
161		    sizeof(from), -1, EINVAL, setup1, cleanup1,
162		    "invalid MSG_OOB flag set"}
163	,
164/* 10 */
165	{
166	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
167		    &msgdat, MSG_ERRQUEUE, (struct sockaddr *)&from,
168		    sizeof(from), -1, EAGAIN, setup1, cleanup1,
169		    "invalid MSG_ERRQUEUE flag set"}
170	,
171/* 11 */
172	{
173	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
174		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
175		    0, EINVAL, setup3, cleanup2, "invalid cmsg length"}
176	,
177/* 12 */
178	{
179	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
180		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
181		    0, 0, setup4, cleanup2, "large cmesg length"}
182,};
183
184int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]);
185
186int exp_enos[] = { EBADF, ENOTSOCK, EFAULT, EINVAL, 0 };
187
188#ifdef UCLINUX
189static char *argv0;
190#endif
191
192int main(int argc, char *argv[])
193{
194	int lc;
195	const char *msg;
196
197	if ((msg = parse_opts(argc, argv, NULL, NULL)) != NULL)
198		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
199#ifdef UCLINUX
200	argv0 = argv[0];
201	maybe_run_child(&do_child, "dd", &sfd, &ufd);
202#endif
203
204	setup();
205
206	TEST_EXP_ENOS(exp_enos);
207
208	for (lc = 0; TEST_LOOPING(lc); ++lc) {
209		tst_count = 0;
210		for (testno = 0; testno < TST_TOTAL; ++testno) {
211			tdat[testno].setup();
212
213			/* setup common to all tests */
214
215			iov[0].iov_base = tdat[testno].buf;
216			iov[0].iov_len = tdat[testno].buflen;
217			msgdat.msg_name = tdat[testno].from;
218			msgdat.msg_namelen = tdat[testno].fromlen;
219			msgdat.msg_iov = tdat[testno].iov;
220			msgdat.msg_iovlen = tdat[testno].iovcnt;
221			msgdat.msg_control = control;
222			msgdat.msg_controllen = controllen;
223			msgdat.msg_flags = 0;
224
225			TEST(recvmsg(s, tdat[testno].msg, tdat[testno].flags));
226			if (TEST_RETURN >= 0)
227				TEST_RETURN = 0;	/* all nonzero equal here */
228			if (TEST_RETURN != tdat[testno].retval ||
229			    (TEST_RETURN < 0 &&
230			     TEST_ERRNO != tdat[testno].experrno)) {
231				tst_resm(TFAIL, "%s ; returned"
232					 " %ld (expected %d), errno %d (expected"
233					 " %d)", tdat[testno].desc,
234					 TEST_RETURN, tdat[testno].retval,
235					 TEST_ERRNO, tdat[testno].experrno);
236			} else {
237				TEST_ERROR_LOG(TEST_ERRNO);
238				tst_resm(TPASS, "%s successful",
239					 tdat[testno].desc);
240			}
241			tdat[testno].cleanup();
242		}
243	}
244	cleanup();
245
246	tst_exit();
247}
248
249pid_t pid;
250char tmpsunpath[1024];
251
252void setup(void)
253{
254	int tfd;
255	TEST_PAUSE;
256
257	tst_tmpdir();
258	(void)strcpy(tmpsunpath, "udsockXXXXXX");
259	tfd = mkstemp(tmpsunpath);
260	close(tfd);
261	unlink(tmpsunpath);
262	sun1.sun_family = AF_UNIX;
263	(void)strcpy(sun1.sun_path, tmpsunpath);
264
265	signal(SIGPIPE, SIG_IGN);
266
267	pid = start_server(&sin1, &sun1);
268}
269
270void cleanup(void)
271{
272	if (pid > 0)
273		(void)kill(pid, SIGKILL);	/* kill server */
274	if (tmpsunpath[0] != '\0')
275		(void)unlink(tmpsunpath);
276	TEST_CLEANUP;
277	tst_rmdir();
278
279}
280
281void setup0(void)
282{
283	if (tdat[testno].experrno == EBADF)
284		s = 400;	/* anything not an open file */
285	else if ((s = open("/dev/null", O_WRONLY)) == -1)
286		tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed");
287}
288
289void cleanup0(void)
290{
291	s = -1;
292}
293
294void setup1(void)
295{
296	fd_set rdfds;
297	struct timeval timeout;
298	int n;
299
300	s = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto);
301	if (s < 0)
302		tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed");
303	if (tdat[testno].type == SOCK_STREAM) {
304		if (tdat[testno].domain == PF_INET) {
305			if (connect(s, (struct sockaddr *)&sin1, sizeof(sin1)) <
306			    0)
307				tst_brkm(TBROK | TERRNO, cleanup,
308					 "connect failed");
309			/* Wait for something to be readable, else we won't detect EFAULT on recv */
310			FD_ZERO(&rdfds);
311			FD_SET(s, &rdfds);
312			timeout.tv_sec = 2;
313			timeout.tv_usec = 0;
314			n = select(s + 1, &rdfds, 0, 0, &timeout);
315			if (n != 1 || !FD_ISSET(s, &rdfds))
316				tst_brkm(TBROK, cleanup,
317					 "client setup1 failed - no message ready in 2 sec");
318		} else if (tdat[testno].domain == PF_UNIX) {
319			if (connect(s, (struct sockaddr *)&sun1, sizeof(sun1)) <
320			    0)
321				tst_brkm(TBROK | TERRNO, cleanup,
322					 "UD connect failed");
323		}
324	}
325}
326
327void setup2(void)
328{
329	setup1();
330	if (write(s, "R", 1) < 0)
331		tst_brkm(TBROK | TERRNO, cleanup, "test setup failed: write:");
332	control = (struct cmsghdr *)cbuf;
333	controllen = control->cmsg_len = sizeof(cbuf);
334}
335
336void setup3(void)
337{
338	setup2();
339	controllen = sizeof(struct cmsghdr) - 1;
340}
341
342void setup4(void)
343{
344	setup2();
345	controllen = 128 * 1024;
346}
347
348void cleanup1(void)
349{
350	(void)close(s);
351	close(ufd);
352	close(sfd);
353	s = -1;
354}
355
356void cleanup2(void)
357{
358	close(ufd);
359	close(sfd);
360	(void)close(s);
361	s = -1;
362
363	if (passed_fd >= 0)
364		(void)close(passed_fd);
365	passed_fd = -1;
366	control = 0;
367	controllen = 0;
368}
369
370pid_t start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun)
371{
372	pid_t pid;
373	socklen_t slen = sizeof(*ssin);
374
375	ssin->sin_family = AF_INET;
376	ssin->sin_port = 0; /* pick random free port */
377	ssin->sin_addr.s_addr = INADDR_ANY;
378
379	/* set up inet socket */
380	sfd = socket(PF_INET, SOCK_STREAM, 0);
381	if (sfd < 0) {
382		tst_brkm(TBROK | TERRNO, cleanup, "server socket failed");
383		return -1;
384	}
385	if (bind(sfd, (struct sockaddr *)ssin, sizeof(*ssin)) < 0) {
386		tst_brkm(TBROK | TERRNO, cleanup, "server bind failed");
387		return -1;
388	}
389	if (listen(sfd, 10) < 0) {
390		tst_brkm(TBROK | TERRNO, cleanup, "server listen failed");
391		return -1;
392	}
393	if (getsockname(sfd, (struct sockaddr *)ssin, &slen) == -1)
394		tst_brkm(TBROK | TERRNO, cleanup, "getsockname failed");
395
396	/* set up UNIX-domain socket */
397	ufd = socket(PF_UNIX, SOCK_STREAM, 0);
398	if (ufd < 0) {
399		tst_brkm(TBROK | TERRNO, cleanup, "server UD socket failed");
400		return -1;
401	}
402	if (bind(ufd, (struct sockaddr *)ssun, sizeof(*ssun))) {
403		tst_brkm(TBROK | TERRNO, cleanup, "server UD bind failed");
404		return -1;
405	}
406	if (listen(ufd, 10) < 0) {
407		tst_brkm(TBROK | TERRNO, cleanup, "server UD listen failed");
408		return -1;
409	}
410
411	switch ((pid = FORK_OR_VFORK())) {
412	case 0:		/* child */
413#ifdef UCLINUX
414		if (self_exec(argv0, "dd", sfd, ufd) < 0)
415			tst_brkm(TBROK | TERRNO, cleanup,
416				 "server self_exec failed");
417#else
418		do_child();
419#endif
420		break;
421	case -1:
422		tst_brkm(TBROK | TERRNO, cleanup, "server fork failed");
423		/* fall through */
424	default:		/* parent */
425		(void)close(sfd);
426		(void)close(ufd);
427		return pid;
428	}
429	exit(1);
430}
431
432void do_child(void)
433{
434	struct sockaddr_in fsin;
435	struct sockaddr_un fsun;
436	fd_set afds, rfds;
437	int nfds, cc, fd;
438
439	FD_ZERO(&afds);
440	FD_SET(sfd, &afds);
441	FD_SET(ufd, &afds);
442
443	nfds = MAX(sfd + 1, ufd + 1);
444
445	/* accept connections until killed */
446	while (1) {
447		socklen_t fromlen;
448
449		memcpy(&rfds, &afds, sizeof(rfds));
450
451		if (select(nfds, &rfds, NULL, NULL,
452			   NULL) < 0) {
453			if (errno != EINTR) {
454				perror("server select");
455				exit(1);
456			}
457			continue;
458		}
459		if (FD_ISSET(sfd, &rfds)) {
460			int newfd;
461
462			fromlen = sizeof(fsin);
463			newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen);
464			if (newfd >= 0) {
465				FD_SET(newfd, &afds);
466				nfds = MAX(nfds, newfd + 1);
467				/* send something back */
468				(void)write(newfd, "hoser\n", 6);
469			}
470		}
471		if (FD_ISSET(ufd, &rfds)) {
472			int newfd;
473
474			fromlen = sizeof(fsun);
475			newfd = accept(ufd, (struct sockaddr *)&fsun, &fromlen);
476			if (newfd >= 0)
477				FD_SET(newfd, &afds);
478		}
479		for (fd = 0; fd < nfds; ++fd)
480			if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) {
481				char rbuf[1024];
482
483				cc = read(fd, rbuf, sizeof(rbuf));
484				if (cc && rbuf[0] == 'R')
485					sender(fd);
486				if (cc == 0 || (cc < 0 && errno != EINTR)) {
487					(void)close(fd);
488					FD_CLR(fd, &afds);
489				}
490			}
491	}
492}
493
494#define TM	"from recvmsg01 server"
495
496/* special for rights-passing test */
497void sender(int fd)
498{
499	struct msghdr mh;
500	struct cmsghdr *control;
501	char tmpfn[1024], snd_cbuf[1024];
502	int tfd;
503
504	(void)strcpy(tmpfn, "smtXXXXXX");
505	tfd = mkstemp(tmpfn);
506	if (tfd < 0)
507		return;
508
509	memset(&mh, 0x00, sizeof(mh));
510
511	/* set up cmsghdr */
512	control = (struct cmsghdr *)snd_cbuf;
513	memset(control, 0x00, sizeof(struct cmsghdr));
514	control->cmsg_len = sizeof(struct cmsghdr) + 4;
515	control->cmsg_level = SOL_SOCKET;
516	control->cmsg_type = SCM_RIGHTS;
517	*(int *)CMSG_DATA(control) = tfd;
518
519	/* set up msghdr */
520	iov[0].iov_base = TM;
521	iov[0].iov_len = sizeof(TM);
522	mh.msg_iov = iov;
523	mh.msg_iovlen = 1;
524	mh.msg_flags = 0;
525	mh.msg_control = control;
526	mh.msg_controllen = control->cmsg_len;
527
528	/* do it */
529	(void)sendmsg(fd, &mh, 0);
530	(void)close(tfd);
531	(void)unlink(tmpfn);
532}
533