recvmsg01.c revision d34d581c6a320e356a6cda923c7aa399479e812c
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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/* The #ifndef code below is for 2.5 64-bit kernels, where     */
44/* the MSG_CMSG_COMPAT flag must be 0 in order for the syscall */
45/* and this test to function correctly.                        */
46#ifndef MSG_CMSG_COMPAT
47
48#if defined (__powerpc64__) || defined (__x86_64__) || defined (__sparc64__)
49#define MSG_CMSG_COMPAT 0x80000000
50#else
51#define MSG_CMSG_COMPAT 0
52#endif
53
54#endif
55/***************************************************/
56
57
58#include <stdio.h>
59#include <unistd.h>
60#include <errno.h>
61#include <fcntl.h>
62
63#include <sys/types.h>
64#include <sys/socket.h>
65#include <sys/signal.h>
66#include <sys/uio.h>
67#include <sys/un.h>
68#include <sys/file.h>
69
70#include <netinet/in.h>
71
72#include "test.h"
73#include "usctest.h"
74
75char *TCID="recvmsg01";		/* Test program identifier.    */
76int testno;
77
78char	buf[1024], cbuf[1024];
79int	s;			/* socket descriptor */
80int	passed_fd = -1;		/* rights-passing test descriptor */
81struct sockaddr_in sin1, from;
82struct sockaddr_un sun1;
83struct msghdr msgdat;
84struct cmsghdr *control = 0;
85int	controllen = 0;
86struct iovec iov[1];
87static int sfd; /* shared between do_child and start_server */
88static int ufd; /* shared between do_child and start_server */
89
90void setup(void);
91void setup0(void);
92void setup1(void);
93void setup2(void);
94void setup3(void);
95void setup4(void);
96void cleanup(void);
97void cleanup0(void);
98void cleanup1(void);
99void cleanup2(void);
100void do_child(void);
101
102void sender(int);
103pid_t start_server(struct sockaddr_in *, struct sockaddr_un *);
104
105struct test_case_t {		/* test case structure */
106	int	domain;	/* PF_INET, PF_UNIX, ... */
107	int	type;	/* SOCK_STREAM, SOCK_DGRAM ... */
108	int	proto;	/* protocol number (usually 0 = default) */
109	struct iovec	*iov;
110	int	iovcnt;
111	void	*buf;	/* recv data buffer */
112	int	buflen;	/* recv buffer length */
113	struct msghdr *msg;
114	unsigned flags;
115	struct sockaddr *from;	/* from address */
116	int	fromlen;	/* from address value/result buffer length */
117	int	retval;		/* syscall return value */
118	int	experrno;	/* expected errno */
119	void	(*setup)(void);
120	void	(*cleanup)(void);
121	char *desc;
122} tdat[] = {
123/* 1 */
124	{ PF_INET, SOCK_STREAM, 0, iov, 1, buf, sizeof(buf), &msgdat, 0,
125		(struct sockaddr *)&from, sizeof(from),
126		-1, EBADF, setup0, cleanup0, "bad file descriptor" },
127/* 2 */
128	{ 0, 0, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0,
129		(struct sockaddr *)&from, sizeof(from),
130		-1, ENOTSOCK, setup0, cleanup0, "invalid socket" },
131/* 3 */
132	{ PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0,
133		(struct sockaddr *)-1, sizeof(from),
134		0, ENOTSOCK, setup1, cleanup1, "invalid socket buffer" },
135/* 4 */
136	{ PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, -1,
137		(struct sockaddr *)&from, -1,
138		-1, EINVAL, setup1, cleanup1, "invalid socket length" },
139/* 5 */
140	{ PF_INET, SOCK_STREAM, 0, iov, 1, (void *)-1, sizeof(buf), &msgdat, 0,
141		(struct sockaddr *)&from, sizeof(from),
142		-1, EFAULT, setup1, cleanup1, "invalid recv buffer" },
143/* 6 */
144	{ PF_INET, SOCK_STREAM, 0, 0, 1, (void *)buf, sizeof(buf), &msgdat, 0,
145		(struct sockaddr *)&from, sizeof(from),
146		-1, EFAULT, setup1, cleanup1, "invalid iovec buffer" },
147/* 7 */
148	{ PF_INET, SOCK_STREAM, 0, iov, -1, (void *)buf, sizeof(buf), &msgdat,0,
149		(struct sockaddr *)&from, sizeof(from),
150		-1, EMSGSIZE, setup1, cleanup1, "invalid iovec count" },
151/* 8 */
152	{ PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,0,
153		(struct sockaddr *)&from, sizeof(from),
154		0, 0, setup2, cleanup2, "rights reception" },
155/* 9 */
156	{ PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,~MSG_CMSG_COMPAT,
157		(struct sockaddr *)&from, sizeof(from),
158		-1, EINVAL, setup1, cleanup1, "invalid flags set" },
159/* 10 */
160	{ PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,0,
161		(struct sockaddr *)&from, sizeof(from),
162		0, EINVAL, setup3, cleanup2, "invalid cmsg length" },
163/* 11 */
164	{ PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat,
165		0, (struct sockaddr *)&from, sizeof(from),
166		0, 0, setup4, cleanup2, "large cmesg length" },
167};
168
169int TST_TOTAL=sizeof(tdat)/sizeof(tdat[0]); /* Total number of test cases. */
170
171int exp_enos[] = {EBADF, ENOTSOCK, EFAULT, EINVAL, 0};
172
173extern int Tst_count;
174
175#ifdef UCLINUX
176static char *argv0;
177#endif
178
179int
180main(int argc, char *argv[])
181{
182	int lc;			/* loop counter */
183	char *msg;		/* message returned from parse_opts */
184
185	/* Parse standard options given to run the test. */
186	msg = parse_opts(argc, argv, (option_t *)NULL, NULL);
187	if (msg != (char *)NULL) {
188		tst_brkm(TBROK, tst_exit, "OPTION PARSING ERROR - %s", msg);
189	}
190
191#ifdef UCLINUX
192	argv0 = argv[0];
193	maybe_run_child(&do_child, "dd", &sfd, &ufd);
194#endif
195
196	setup();
197
198	TEST_EXP_ENOS(exp_enos);
199
200	/* Check looping state if -i option given */
201	for (lc = 0; TEST_LOOPING(lc); ++lc) {
202		Tst_count = 0;
203		for (testno=0; testno < TST_TOTAL; ++testno) {
204			tdat[testno].setup();
205
206			/* setup common to all tests */
207
208			iov[0].iov_base = tdat[testno].buf;
209			iov[0].iov_len = tdat[testno].buflen;
210			msgdat.msg_name = tdat[testno].from;
211			msgdat.msg_namelen = tdat[testno].fromlen;
212			msgdat.msg_iov = tdat[testno].iov;
213			msgdat.msg_iovlen = tdat[testno].iovcnt;
214			msgdat.msg_control = control;
215			msgdat.msg_controllen = controllen;
216			msgdat.msg_flags = 0;
217
218			TEST(recvmsg(s, tdat[testno].msg, tdat[testno].flags));
219			if (TEST_RETURN >= 0)
220				TEST_RETURN = 0; /* all nonzero equal here */
221			if (TEST_RETURN != tdat[testno].retval ||
222			    (TEST_RETURN < 0 &&
223			     TEST_ERRNO != tdat[testno].experrno)) {
224				tst_resm(TFAIL, "%s ; returned"
225					" %d (expected %d), errno %d (expected"
226					" %d)", tdat[testno].desc,
227					TEST_RETURN, tdat[testno].retval,
228					TEST_ERRNO, tdat[testno].experrno);
229			} else {
230				TEST_ERROR_LOG(TEST_ERRNO);
231				tst_resm(TPASS, "%s successful",
232					tdat[testno].desc);
233			}
234			tdat[testno].cleanup();
235		}
236	}
237	cleanup();
238	/*NOT REACHED*/
239	return 0;
240}	/* End main */
241
242pid_t pid;
243char tmpsunpath[1024];
244
245void
246setup(void)
247{
248	int tfd;
249	TEST_PAUSE;	/* if -P option specified */
250
251	/* initialize sockaddr's */
252	sin1.sin_family = AF_INET;
253	sin1.sin_port = htons((getpid() % 32768) +11000);
254	sin1.sin_addr.s_addr = INADDR_ANY;
255	tst_tmpdir();
256	(void) strcpy(tmpsunpath, "udsockXXXXXX");
257	tfd = mkstemp(tmpsunpath);
258	close(tfd);
259	unlink(tmpsunpath);
260	sun1.sun_family = AF_UNIX;
261	(void) strcpy(sun1.sun_path, tmpsunpath);
262
263	signal(SIGPIPE, SIG_IGN);
264
265	pid = start_server(&sin1, &sun1);
266}
267
268void
269cleanup(void)
270{
271	(void) kill(pid, SIGKILL);	/* kill server */
272	if (tmpsunpath[0] != '\0')
273		(void) unlink(tmpsunpath);
274	TEST_CLEANUP;
275	tst_rmdir();
276	tst_exit();
277}
278
279
280void
281setup0(void)
282{
283	if (tdat[testno].experrno == EBADF)
284		s = 400;	/* anything not an open file */
285	else
286		if((s = open("/dev/null", O_WRONLY)) == -1)
287			tst_brkm(TBROK, cleanup, "error opening /dev/null - "
288			"errno: %s", strerror(errno));
289}
290
291void
292cleanup0(void)
293{
294	s = -1;
295}
296
297void
298setup1(void)
299{
300        fd_set rdfds;
301        struct timeval timeout;
302        int n;
303
304	s = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto);
305	if (s < 0) {
306		tst_brkm(TBROK, cleanup, "socket setup failed: %s",
307			strerror(errno));
308	}
309	if (tdat[testno].type == SOCK_STREAM) {
310		if (tdat[testno].domain == PF_INET) {
311	    		if (connect(s, (struct sockaddr*)&sin1, sizeof(sin1))<0)
312				tst_brkm(TBROK, cleanup, "connect failed: %s ",
313					strerror(errno));
314        		/* Wait for something to be readable, else we won't detect EFAULT on recv */
315		        FD_ZERO(&rdfds);
316		        FD_SET(s, &rdfds);
317		        timeout.tv_sec = 2;
318		        timeout.tv_usec = 0;
319		        n = select(s+1, &rdfds, 0, 0, &timeout);
320		        if (n != 1 || !FD_ISSET(s, &rdfds))
321		                tst_brkm(TBROK, cleanup, "client setup1 failed - no message ready in 2 sec");
322		} else if (tdat[testno].domain == PF_UNIX) {
323	    		if (connect(s, (struct sockaddr*)&sun1, sizeof(sun1))<0)
324				tst_brkm(TBROK, cleanup, "UD connect failed:",
325					" %s ", strerror(errno));
326		}
327	}
328}
329
330void
331setup2(void)
332{
333	setup1();
334	if (write(s, "R", 1) < 0) {
335		tst_brkm(TBROK, cleanup, "test setup failed: write: %s",
336			strerror(errno));
337	}
338	control = (struct cmsghdr *)cbuf;
339	controllen = control->cmsg_len = sizeof(cbuf);
340}
341
342void
343setup3(void)
344{
345	setup2();
346	controllen = sizeof(struct cmsghdr) -1;
347}
348
349void
350setup4(void)
351{
352	setup2();
353	controllen = 128 * 1024;
354}
355
356void
357cleanup1(void)
358{
359	(void) close(s);
360	s = -1;
361}
362
363void
364cleanup2(void)
365{
366	(void) close(s);
367	s = -1;
368
369	if (passed_fd >= 0)
370		(void) close(passed_fd);
371	passed_fd = -1;
372	control = 0;
373	controllen = 0;
374}
375
376pid_t
377start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun)
378{
379	pid_t	pid;
380
381	/* set up inet socket */
382	sfd = socket(PF_INET, SOCK_STREAM, 0);
383	if (sfd < 0) {
384		tst_brkm(TBROK, cleanup, "server socket failed: %s",
385			strerror(errno));
386		return -1;
387	}
388	if (bind(sfd, (struct sockaddr *)ssin, sizeof(*ssin)) < 0) {
389		tst_brkm(TBROK, cleanup, "server bind failed: %s",
390			strerror(errno));
391		return -1;
392	}
393	if (listen(sfd, 10) < 0) {
394		tst_brkm(TBROK, cleanup, "server listen failed: %s",
395			strerror(errno));
396		return -1;
397	}
398	/* set up UNIX-domain socket */
399	ufd = socket(PF_UNIX, SOCK_STREAM, 0);
400	if (ufd < 0) {
401		tst_brkm(TBROK, cleanup, "server UD socket failed: %s",
402			strerror(errno));
403		return -1;
404	}
405	if (bind(ufd, (struct sockaddr *)ssun, sizeof(*ssun))) {
406		tst_brkm(TBROK, cleanup, "server UD bind failed: %s",
407			strerror(errno));
408		return -1;
409	}
410	if (listen(ufd, 10) < 0) {
411		tst_brkm(TBROK, cleanup, "server UD listen failed: %s",
412			strerror(errno));
413		return -1;
414	}
415
416	switch ((pid = fork())) {
417	case 0:	/* child */
418#ifdef UCLINUX
419		if (self_exec(argv0, "dd", sfd, ufd) < 0) {
420			tst_brkm(TBROK, cleanup, "server self_exec failed");
421		}
422#else
423		do_child();
424#endif
425		break;
426	case -1:
427		tst_brkm(TBROK, cleanup, "server fork failed: %s",
428			strerror(errno));
429		/* fall through */
430	default: /* parent */
431		(void) close(sfd);
432		(void) close(ufd);
433		return pid;
434	}
435	/*NOTREACHED*/
436	exit(1);
437}
438
439void
440do_child()
441{
442	struct sockaddr_in fsin;
443	struct sockaddr_un fsun;
444	fd_set	afds, rfds;
445	int nfds, cc, fd;
446
447
448	FD_ZERO(&afds);
449	FD_SET(sfd, &afds);
450	FD_SET(ufd, &afds);
451
452	nfds = getdtablesize();
453
454	/* accept connections until killed */
455	while (1) {
456		int	fromlen;
457
458		memcpy(&rfds, &afds, sizeof(rfds));
459
460		if (select(nfds, &rfds, (fd_set *)0, (fd_set *)0,
461		    (struct timeval *)0) < 0) {
462			if (errno != EINTR) {
463				perror("server select");
464				exit(1);
465			}
466			continue;
467		}
468		if (FD_ISSET(sfd, &rfds)) {
469			int newfd;
470
471			fromlen = sizeof(fsin);
472			newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen);
473			if (newfd >= 0) {
474				FD_SET(newfd, &afds);
475				/* send something back */
476				(void) write(newfd, "hoser\n", 6);
477			}
478		}
479		if (FD_ISSET(ufd, &rfds)) {
480			int newfd;
481
482			fromlen = sizeof(fsun);
483			newfd = accept(ufd, (struct sockaddr *)&fsun, &fromlen);
484			if (newfd >= 0)
485				FD_SET(newfd, &afds);
486		}
487		for (fd=0; fd<nfds; ++fd)
488			if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) {
489				char	rbuf[1024];
490
491				cc = read(fd, rbuf, sizeof(rbuf));
492				if (cc && rbuf[0] == 'R')
493					sender(fd);
494				if (cc == 0 || (cc < 0 && errno != EINTR)) {
495					(void) close(fd);
496					FD_CLR(fd, &afds);
497				}
498			}
499	}
500}
501
502#define TM	"from recvmsg01 server"
503
504/* special for rights-passing test */
505void
506sender(int fd)
507{
508	struct msghdr mh;
509	struct cmsghdr *control;
510	char	tmpfn[1024], snd_cbuf[1024];
511	int	tfd;
512
513	(void) strcpy(tmpfn, "smtXXXXXX");
514	tfd = mkstemp(tmpfn);
515	if (tfd < 0)
516		return;
517	(void) unlink(tmpfn);
518
519	bzero(&mh, sizeof(mh));
520
521	/* set up cmsghdr */
522	control = (struct cmsghdr *)snd_cbuf;
523	bzero(control, sizeof(struct cmsghdr));
524	control->cmsg_len = sizeof(struct cmsghdr)+4;
525	control->cmsg_level = SOL_SOCKET;
526	control->cmsg_type = SCM_RIGHTS;
527	*(int *)CMSG_DATA(control) = tfd;
528
529	/* set up msghdr */
530	iov[0].iov_base = TM;
531	iov[0].iov_len = sizeof(TM);
532	mh.msg_iov = iov;
533	mh.msg_iovlen = 1;
534	mh.msg_flags = 0;
535	mh.msg_control = control;
536	mh.msg_controllen = control->cmsg_len;
537
538	/* do it */
539	(void) sendmsg(fd, &mh, 0);
540	(void) close(tfd);
541}
542