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