recvmsg01.c revision c57fba5535abf457e33dd7a986b6c512d95cdef6
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/* 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 (__mips64) || 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#include <stdio.h>
58#include <unistd.h>
59#include <string.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";
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	{
125	PF_INET, SOCK_STREAM, 0, iov, 1, buf, sizeof(buf), &msgdat, 0,
126		    (struct sockaddr *)&from, sizeof(from),
127		    -1, EBADF, setup0, cleanup0, "bad file descriptor"}
128	,
129/* 2 */
130	{
131	0, 0, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0,
132		    (struct sockaddr *)&from, sizeof(from),
133		    -1, ENOTSOCK, setup0, cleanup0, "invalid socket"}
134	,
135/* 3 */
136	{
137	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
138		    &msgdat, 0, (struct sockaddr *)-1, sizeof(from), 0,
139		    ENOTSOCK, setup1, cleanup1, "invalid socket buffer"}
140	,
141/* 4 */
142	{
143	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
144		    &msgdat, -1, (struct sockaddr *)&from, -1, -1,
145		    EINVAL, setup1, cleanup1, "invalid socket length"},
146/* 5 */
147	{
148	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)-1, sizeof(buf),
149		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
150		    -1, EFAULT, setup1, cleanup1, "invalid recv buffer"}
151	,
152/* 6 */
153	{
154	PF_INET, SOCK_STREAM, 0, 0, 1, (void *)buf, sizeof(buf),
155		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
156		    -1, EFAULT, setup1, cleanup1, "invalid iovec buffer"}
157	,
158/* 7 */
159	{
160	PF_INET, SOCK_STREAM, 0, iov, -1, (void *)buf, sizeof(buf),
161		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
162		    -1, EMSGSIZE, setup1, cleanup1, "invalid iovec count"}
163	,
164/* 8 */
165	{
166	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
167		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
168		    0, 0, setup2, cleanup2, "rights reception"}
169	,
170/* 9 */
171	{
172	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
173		    &msgdat, ~MSG_CMSG_COMPAT, (struct sockaddr *)&from,
174		    sizeof(from), -1, EINVAL, setup1, cleanup1,
175		    "invalid flags set"}
176	,
177/* 10 */
178	{
179	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
180		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
181		    0, EINVAL, setup3, cleanup2, "invalid cmsg length"}
182	,
183/* 11 */
184	{
185	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
186		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
187		    0, 0, setup4, cleanup2, "large cmesg length"}
188,};
189
190int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]);
191
192int exp_enos[] = { EBADF, ENOTSOCK, EFAULT, EINVAL, 0 };
193
194#ifdef UCLINUX
195static char *argv0;
196#endif
197
198int main(int argc, char *argv[])
199{
200	int lc;
201	char *msg;
202
203	if ((msg = parse_opts(argc, argv, NULL, NULL)) != NULL)
204		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
205#ifdef UCLINUX
206	argv0 = argv[0];
207	maybe_run_child(&do_child, "dd", &sfd, &ufd);
208#endif
209
210	setup();
211
212	TEST_EXP_ENOS(exp_enos);
213
214	for (lc = 0; TEST_LOOPING(lc); ++lc) {
215		tst_count = 0;
216		for (testno = 0; testno < TST_TOTAL; ++testno) {
217			tdat[testno].setup();
218
219			/* setup common to all tests */
220
221			iov[0].iov_base = tdat[testno].buf;
222			iov[0].iov_len = tdat[testno].buflen;
223			msgdat.msg_name = tdat[testno].from;
224			msgdat.msg_namelen = tdat[testno].fromlen;
225			msgdat.msg_iov = tdat[testno].iov;
226			msgdat.msg_iovlen = tdat[testno].iovcnt;
227			msgdat.msg_control = control;
228			msgdat.msg_controllen = controllen;
229			msgdat.msg_flags = 0;
230
231			TEST(recvmsg(s, tdat[testno].msg, tdat[testno].flags));
232			if (TEST_RETURN >= 0)
233				TEST_RETURN = 0;	/* all nonzero equal here */
234			if (TEST_RETURN != tdat[testno].retval ||
235			    (TEST_RETURN < 0 &&
236			     TEST_ERRNO != tdat[testno].experrno)) {
237				tst_resm(TFAIL, "%s ; returned"
238					 " %ld (expected %d), errno %d (expected"
239					 " %d)", tdat[testno].desc,
240					 TEST_RETURN, tdat[testno].retval,
241					 TEST_ERRNO, tdat[testno].experrno);
242			} else {
243				TEST_ERROR_LOG(TEST_ERRNO);
244				tst_resm(TPASS, "%s successful",
245					 tdat[testno].desc);
246			}
247			tdat[testno].cleanup();
248		}
249	}
250	cleanup();
251
252	tst_exit();
253}
254
255pid_t pid;
256char tmpsunpath[1024];
257
258void setup(void)
259{
260	int tfd;
261	TEST_PAUSE;
262
263	tst_tmpdir();
264	(void)strcpy(tmpsunpath, "udsockXXXXXX");
265	tfd = mkstemp(tmpsunpath);
266	close(tfd);
267	unlink(tmpsunpath);
268	sun1.sun_family = AF_UNIX;
269	(void)strcpy(sun1.sun_path, tmpsunpath);
270
271	signal(SIGPIPE, SIG_IGN);
272
273	pid = start_server(&sin1, &sun1);
274}
275
276void cleanup(void)
277{
278	if (pid > 0)
279		(void)kill(pid, SIGKILL);	/* kill server */
280	if (tmpsunpath[0] != '\0')
281		(void)unlink(tmpsunpath);
282	TEST_CLEANUP;
283	tst_rmdir();
284
285}
286
287void setup0(void)
288{
289	if (tdat[testno].experrno == EBADF)
290		s = 400;	/* anything not an open file */
291	else if ((s = open("/dev/null", O_WRONLY)) == -1)
292		tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed");
293}
294
295void cleanup0(void)
296{
297	s = -1;
298}
299
300void setup1(void)
301{
302	fd_set rdfds;
303	struct timeval timeout;
304	int n;
305
306	s = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto);
307	if (s < 0)
308		tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed");
309	if (tdat[testno].type == SOCK_STREAM) {
310		if (tdat[testno].domain == PF_INET) {
311			if (connect(s, (struct sockaddr *)&sin1, sizeof(sin1)) <
312			    0)
313				tst_brkm(TBROK | TERRNO, cleanup,
314					 "connect failed");
315			/* Wait for something to be readable, else we won't detect EFAULT on recv */
316			FD_ZERO(&rdfds);
317			FD_SET(s, &rdfds);
318			timeout.tv_sec = 2;
319			timeout.tv_usec = 0;
320			n = select(s + 1, &rdfds, 0, 0, &timeout);
321			if (n != 1 || !FD_ISSET(s, &rdfds))
322				tst_brkm(TBROK, cleanup,
323					 "client setup1 failed - no message ready in 2 sec");
324		} else if (tdat[testno].domain == PF_UNIX) {
325			if (connect(s, (struct sockaddr *)&sun1, sizeof(sun1)) <
326			    0)
327				tst_brkm(TBROK | TERRNO, cleanup,
328					 "UD connect failed");
329		}
330	}
331}
332
333void setup2(void)
334{
335	setup1();
336	if (write(s, "R", 1) < 0)
337		tst_brkm(TBROK | TERRNO, cleanup, "test setup failed: write:");
338	control = (struct cmsghdr *)cbuf;
339	controllen = control->cmsg_len = sizeof(cbuf);
340}
341
342void setup3(void)
343{
344	setup2();
345	controllen = sizeof(struct cmsghdr) - 1;
346}
347
348void setup4(void)
349{
350	setup2();
351	controllen = 128 * 1024;
352}
353
354void cleanup1(void)
355{
356	(void)close(s);
357	close(ufd);
358	close(sfd);
359	s = -1;
360}
361
362void cleanup2(void)
363{
364	close(ufd);
365	close(sfd);
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 start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun)
377{
378	pid_t pid;
379	socklen_t slen = sizeof(*ssin);
380
381	ssin->sin_family = AF_INET;
382	ssin->sin_port = 0; /* pick random free port */
383	ssin->sin_addr.s_addr = INADDR_ANY;
384
385	/* set up inet socket */
386	sfd = socket(PF_INET, SOCK_STREAM, 0);
387	if (sfd < 0) {
388		tst_brkm(TBROK | TERRNO, cleanup, "server socket failed");
389		return -1;
390	}
391	if (bind(sfd, (struct sockaddr *)ssin, sizeof(*ssin)) < 0) {
392		tst_brkm(TBROK | TERRNO, cleanup, "server bind failed");
393		return -1;
394	}
395	if (listen(sfd, 10) < 0) {
396		tst_brkm(TBROK | TERRNO, cleanup, "server listen failed");
397		return -1;
398	}
399	if (getsockname(sfd, (struct sockaddr *)ssin, &slen) == -1)
400		tst_brkm(TBROK | TERRNO, cleanup, "getsockname failed");
401
402	/* set up UNIX-domain socket */
403	ufd = socket(PF_UNIX, SOCK_STREAM, 0);
404	if (ufd < 0) {
405		tst_brkm(TBROK | TERRNO, cleanup, "server UD socket failed");
406		return -1;
407	}
408	if (bind(ufd, (struct sockaddr *)ssun, sizeof(*ssun))) {
409		tst_brkm(TBROK | TERRNO, cleanup, "server UD bind failed");
410		return -1;
411	}
412	if (listen(ufd, 10) < 0) {
413		tst_brkm(TBROK | TERRNO, cleanup, "server UD listen failed");
414		return -1;
415	}
416
417	switch ((pid = FORK_OR_VFORK())) {
418	case 0:		/* child */
419#ifdef UCLINUX
420		if (self_exec(argv0, "dd", sfd, ufd) < 0)
421			tst_brkm(TBROK | TERRNO, cleanup,
422				 "server self_exec failed");
423#else
424		do_child();
425#endif
426		break;
427	case -1:
428		tst_brkm(TBROK | TERRNO, cleanup, "server fork failed");
429		/* fall through */
430	default:		/* parent */
431		(void)close(sfd);
432		(void)close(ufd);
433		return pid;
434	}
435	exit(1);
436}
437
438void do_child(void)
439{
440	struct sockaddr_in fsin;
441	struct sockaddr_un fsun;
442	fd_set afds, rfds;
443	int nfds, cc, fd;
444
445	FD_ZERO(&afds);
446	FD_SET(sfd, &afds);
447	FD_SET(ufd, &afds);
448
449	nfds = getdtablesize();
450
451	/* accept connections until killed */
452	while (1) {
453		socklen_t fromlen;
454
455		memcpy(&rfds, &afds, sizeof(rfds));
456
457		if (select(nfds, &rfds, (fd_set *) 0, (fd_set *) 0,
458			   (struct timeval *)0) < 0) {
459			if (errno != EINTR) {
460				perror("server select");
461				exit(1);
462			}
463			continue;
464		}
465		if (FD_ISSET(sfd, &rfds)) {
466			int newfd;
467
468			fromlen = sizeof(fsin);
469			newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen);
470			if (newfd >= 0) {
471				FD_SET(newfd, &afds);
472				/* send something back */
473				(void)write(newfd, "hoser\n", 6);
474			}
475		}
476		if (FD_ISSET(ufd, &rfds)) {
477			int newfd;
478
479			fromlen = sizeof(fsun);
480			newfd = accept(ufd, (struct sockaddr *)&fsun, &fromlen);
481			if (newfd >= 0)
482				FD_SET(newfd, &afds);
483		}
484		for (fd = 0; fd < nfds; ++fd)
485			if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) {
486				char rbuf[1024];
487
488				cc = read(fd, rbuf, sizeof(rbuf));
489				if (cc && rbuf[0] == 'R')
490					sender(fd);
491				if (cc == 0 || (cc < 0 && errno != EINTR)) {
492					(void)close(fd);
493					FD_CLR(fd, &afds);
494				}
495			}
496	}
497}
498
499#define TM	"from recvmsg01 server"
500
501/* special for rights-passing test */
502void sender(int fd)
503{
504	struct msghdr mh;
505	struct cmsghdr *control;
506	char tmpfn[1024], snd_cbuf[1024];
507	int tfd;
508
509	(void)strcpy(tmpfn, "smtXXXXXX");
510	tfd = mkstemp(tmpfn);
511	if (tfd < 0)
512		return;
513
514	memset(&mh, 0x00, sizeof(mh));
515
516	/* set up cmsghdr */
517	control = (struct cmsghdr *)snd_cbuf;
518	memset(control, 0x00, sizeof(struct cmsghdr));
519	control->cmsg_len = sizeof(struct cmsghdr) + 4;
520	control->cmsg_level = SOL_SOCKET;
521	control->cmsg_type = SCM_RIGHTS;
522	*(int *)CMSG_DATA(control) = tfd;
523
524	/* set up msghdr */
525	iov[0].iov_base = TM;
526	iov[0].iov_len = sizeof(TM);
527	mh.msg_iov = iov;
528	mh.msg_iovlen = 1;
529	mh.msg_flags = 0;
530	mh.msg_control = control;
531	mh.msg_controllen = control->cmsg_len;
532
533	/* do it */
534	(void)sendmsg(fd, &mh, 0);
535	(void)close(tfd);
536	(void)unlink(tmpfn);
537}
538