Home | History | Annotate | Download | only in recvmsg
      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 "safe_macros.h"
     60 
     61 char *TCID = "recvmsg01";
     62 int testno;
     63 
     64 char buf[1024], cbuf[1024];
     65 int s;				/* socket descriptor */
     66 int passed_fd = -1;		/* rights-passing test descriptor */
     67 struct sockaddr_in sin1, from;
     68 struct sockaddr_un sun1;
     69 struct msghdr msgdat;
     70 struct cmsghdr *control = 0;
     71 int controllen = 0;
     72 struct iovec iov[1];
     73 static int sfd;			/* shared between do_child and start_server */
     74 static int ufd;			/* shared between do_child and start_server */
     75 
     76 void setup(void);
     77 void setup0(void);
     78 void setup1(void);
     79 void setup2(void);
     80 void setup3(void);
     81 void setup4(void);
     82 void cleanup(void);
     83 void cleanup0(void);
     84 void cleanup1(void);
     85 void cleanup2(void);
     86 void do_child(void);
     87 
     88 void sender(int);
     89 pid_t start_server(struct sockaddr_in *, struct sockaddr_un *);
     90 
     91 struct test_case_t {		/* test case structure */
     92 	int domain;		/* PF_INET, PF_UNIX, ... */
     93 	int type;		/* SOCK_STREAM, SOCK_DGRAM ... */
     94 	int proto;		/* protocol number (usually 0 = default) */
     95 	struct iovec *iov;
     96 	int iovcnt;
     97 	void *buf;		/* recv data buffer */
     98 	int buflen;		/* recv buffer length */
     99 	struct msghdr *msg;
    100 	unsigned flags;
    101 	struct sockaddr *from;	/* from address */
    102 	int fromlen;		/* from address value/result buffer length */
    103 	int retval;		/* syscall return value */
    104 	int experrno;		/* expected errno */
    105 	void (*setup) (void);
    106 	void (*cleanup) (void);
    107 	char *desc;
    108 } tdat[] = {
    109 /* 1 */
    110 	{
    111 	PF_INET, SOCK_STREAM, 0, iov, 1, buf, sizeof(buf), &msgdat, 0,
    112 		    (struct sockaddr *)&from, sizeof(from),
    113 		    -1, EBADF, setup0, cleanup0, "bad file descriptor"}
    114 	,
    115 /* 2 */
    116 	{
    117 	0, 0, 0, iov, 1, (void *)buf, sizeof(buf), &msgdat, 0,
    118 		    (struct sockaddr *)&from, sizeof(from),
    119 		    -1, ENOTSOCK, setup0, cleanup0, "invalid socket"}
    120 	,
    121 /* 3 */
    122 	{
    123 	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
    124 		    &msgdat, 0, (struct sockaddr *)-1, sizeof(from), 0,
    125 		    ENOTSOCK, setup1, cleanup1, "invalid socket buffer"}
    126 	,
    127 /* 4 */
    128 	{
    129 	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
    130 		    &msgdat, -1, (struct sockaddr *)&from, -1, -1,
    131 		    EINVAL, setup1, cleanup1, "invalid socket length"},
    132 /* 5 */
    133 	{
    134 	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)-1, sizeof(buf),
    135 		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
    136 		    -1, EFAULT, setup1, cleanup1, "invalid recv buffer"}
    137 	,
    138 /* 6 */
    139 	{
    140 	PF_INET, SOCK_STREAM, 0, 0, 1, (void *)buf, sizeof(buf),
    141 		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
    142 		    -1, EFAULT, setup1, cleanup1, "invalid iovec buffer"}
    143 	,
    144 /* 7 */
    145 	{
    146 	PF_INET, SOCK_STREAM, 0, iov, -1, (void *)buf, sizeof(buf),
    147 		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
    148 		    -1, EMSGSIZE, setup1, cleanup1, "invalid iovec count"}
    149 	,
    150 /* 8 */
    151 	{
    152 	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
    153 		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
    154 		    0, 0, setup2, cleanup2, "rights reception"}
    155 	,
    156 /* 9 */
    157 	{
    158 	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
    159 		    &msgdat, MSG_OOB, (struct sockaddr *)&from,
    160 		    sizeof(from), -1, EINVAL, setup1, cleanup1,
    161 		    "invalid MSG_OOB flag set"}
    162 	,
    163 /* 10 */
    164 	{
    165 	PF_INET, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
    166 		    &msgdat, MSG_ERRQUEUE, (struct sockaddr *)&from,
    167 		    sizeof(from), -1, EAGAIN, setup1, cleanup1,
    168 		    "invalid MSG_ERRQUEUE flag set"}
    169 	,
    170 /* 11 */
    171 	{
    172 	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
    173 		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
    174 		    0, EINVAL, setup3, cleanup2, "invalid cmsg length"}
    175 	,
    176 /* 12 */
    177 	{
    178 	PF_UNIX, SOCK_STREAM, 0, iov, 1, (void *)buf, sizeof(buf),
    179 		    &msgdat, 0, (struct sockaddr *)&from, sizeof(from),
    180 		    0, 0, setup4, cleanup2, "large cmesg length"}
    181 ,};
    182 
    183 int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]);
    184 
    185 #ifdef UCLINUX
    186 static char *argv0;
    187 #endif
    188 
    189 int main(int argc, char *argv[])
    190 {
    191 	int lc;
    192 
    193 	tst_parse_opts(argc, argv, NULL, NULL);
    194 #ifdef UCLINUX
    195 	argv0 = argv[0];
    196 	maybe_run_child(&do_child, "dd", &sfd, &ufd);
    197 #endif
    198 
    199 	setup();
    200 
    201 	for (lc = 0; TEST_LOOPING(lc); ++lc) {
    202 		tst_count = 0;
    203 		for (testno = 0; testno < TST_TOTAL; ++testno) {
    204 			if ((tst_kvercmp(3, 17, 0) < 0)
    205 			    && (tdat[testno].flags & MSG_ERRQUEUE)
    206 			    && (tdat[testno].type & SOCK_STREAM)) {
    207 				tst_resm(TCONF, "skip MSG_ERRQUEUE test, "
    208 						"it's supported from 3.17");
    209 				continue;
    210 			}
    211 
    212 			tdat[testno].setup();
    213 
    214 			/* setup common to all tests */
    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 				tst_resm(TPASS, "%s successful",
    238 					 tdat[testno].desc);
    239 			}
    240 			tdat[testno].cleanup();
    241 		}
    242 	}
    243 	cleanup();
    244 
    245 	tst_exit();
    246 }
    247 
    248 pid_t pid;
    249 char tmpsunpath[1024];
    250 
    251 void setup(void)
    252 {
    253 	int tfd;
    254 	TEST_PAUSE;
    255 
    256 	tst_tmpdir();
    257 	(void)strcpy(tmpsunpath, "udsockXXXXXX");
    258 	tfd = mkstemp(tmpsunpath);
    259 	close(tfd);
    260 	unlink(tmpsunpath);
    261 	sun1.sun_family = AF_UNIX;
    262 	(void)strcpy(sun1.sun_path, tmpsunpath);
    263 
    264 	signal(SIGPIPE, SIG_IGN);
    265 
    266 	pid = start_server(&sin1, &sun1);
    267 }
    268 
    269 void cleanup(void)
    270 {
    271 	if (pid > 0)
    272 		(void)kill(pid, SIGKILL);	/* kill server */
    273 	if (tmpsunpath[0] != '\0')
    274 		(void)unlink(tmpsunpath);
    275 	tst_rmdir();
    276 
    277 }
    278 
    279 void setup0(void)
    280 {
    281 	if (tdat[testno].experrno == EBADF)
    282 		s = 400;	/* anything not an open file */
    283 	else if ((s = open("/dev/null", O_WRONLY)) == -1)
    284 		tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed");
    285 }
    286 
    287 void cleanup0(void)
    288 {
    289 	s = -1;
    290 }
    291 
    292 void setup1(void)
    293 {
    294 	fd_set rdfds;
    295 	struct timeval timeout;
    296 	int n;
    297 
    298 	s = SAFE_SOCKET(cleanup, tdat[testno].domain, tdat[testno].type,
    299 			tdat[testno].proto);
    300 	if (tdat[testno].type == SOCK_STREAM) {
    301 		if (tdat[testno].domain == PF_INET) {
    302 			SAFE_CONNECT(cleanup, s, (struct sockaddr *)&sin1,
    303 				     sizeof(sin1));
    304 			/* Wait for something to be readable, else we won't detect EFAULT on recv */
    305 			FD_ZERO(&rdfds);
    306 			FD_SET(s, &rdfds);
    307 			timeout.tv_sec = 2;
    308 			timeout.tv_usec = 0;
    309 			n = select(s + 1, &rdfds, 0, 0, &timeout);
    310 			if (n != 1 || !FD_ISSET(s, &rdfds))
    311 				tst_brkm(TBROK, cleanup,
    312 					 "client setup1 failed - no message ready in 2 sec");
    313 		} else if (tdat[testno].domain == PF_UNIX) {
    314 			SAFE_CONNECT(cleanup, s, (struct sockaddr *)&sun1,
    315 				     sizeof(sun1));
    316 		}
    317 	}
    318 }
    319 
    320 void setup2(void)
    321 {
    322 	setup1();
    323 	if (write(s, "R", 1) < 0)
    324 		tst_brkm(TBROK | TERRNO, cleanup, "test setup failed: write:");
    325 	control = (struct cmsghdr *)cbuf;
    326 	controllen = control->cmsg_len = sizeof(cbuf);
    327 }
    328 
    329 void setup3(void)
    330 {
    331 	setup2();
    332 	controllen = sizeof(struct cmsghdr) - 1;
    333 }
    334 
    335 void setup4(void)
    336 {
    337 	setup2();
    338 	controllen = 128 * 1024;
    339 }
    340 
    341 void cleanup1(void)
    342 {
    343 	(void)close(s);
    344 	close(ufd);
    345 	close(sfd);
    346 	s = -1;
    347 }
    348 
    349 void cleanup2(void)
    350 {
    351 	close(ufd);
    352 	close(sfd);
    353 	(void)close(s);
    354 	s = -1;
    355 
    356 	if (passed_fd >= 0)
    357 		(void)close(passed_fd);
    358 	passed_fd = -1;
    359 	control = 0;
    360 	controllen = 0;
    361 }
    362 
    363 pid_t start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun)
    364 {
    365 	pid_t pid;
    366 	socklen_t slen = sizeof(*ssin);
    367 
    368 	ssin->sin_family = AF_INET;
    369 	ssin->sin_port = 0; /* pick random free port */
    370 	ssin->sin_addr.s_addr = INADDR_ANY;
    371 
    372 	/* set up inet socket */
    373 	sfd = socket(PF_INET, SOCK_STREAM, 0);
    374 	if (sfd < 0) {
    375 		tst_brkm(TBROK | TERRNO, cleanup, "server socket failed");
    376 		return -1;
    377 	}
    378 	if (bind(sfd, (struct sockaddr *)ssin, sizeof(*ssin)) < 0) {
    379 		tst_brkm(TBROK | TERRNO, cleanup, "server bind failed");
    380 		return -1;
    381 	}
    382 	if (listen(sfd, 10) < 0) {
    383 		tst_brkm(TBROK | TERRNO, cleanup, "server listen failed");
    384 		return -1;
    385 	}
    386 	SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)ssin, &slen);
    387 
    388 	/* set up UNIX-domain socket */
    389 	ufd = socket(PF_UNIX, SOCK_STREAM, 0);
    390 	if (ufd < 0) {
    391 		tst_brkm(TBROK | TERRNO, cleanup, "server UD socket failed");
    392 		return -1;
    393 	}
    394 	if (bind(ufd, (struct sockaddr *)ssun, sizeof(*ssun))) {
    395 		tst_brkm(TBROK | TERRNO, cleanup, "server UD bind failed");
    396 		return -1;
    397 	}
    398 	if (listen(ufd, 10) < 0) {
    399 		tst_brkm(TBROK | TERRNO, cleanup, "server UD listen failed");
    400 		return -1;
    401 	}
    402 
    403 	switch ((pid = FORK_OR_VFORK())) {
    404 	case 0:		/* child */
    405 #ifdef UCLINUX
    406 		if (self_exec(argv0, "dd", sfd, ufd) < 0)
    407 			tst_brkm(TBROK | TERRNO, cleanup,
    408 				 "server self_exec failed");
    409 #else
    410 		do_child();
    411 #endif
    412 		break;
    413 	case -1:
    414 		tst_brkm(TBROK | TERRNO, cleanup, "server fork failed");
    415 		/* fall through */
    416 	default:		/* parent */
    417 		(void)close(sfd);
    418 		(void)close(ufd);
    419 		return pid;
    420 	}
    421 	exit(1);
    422 }
    423 
    424 void do_child(void)
    425 {
    426 	struct sockaddr_in fsin;
    427 	struct sockaddr_un fsun;
    428 	fd_set afds, rfds;
    429 	int nfds, cc, fd;
    430 
    431 	FD_ZERO(&afds);
    432 	FD_SET(sfd, &afds);
    433 	FD_SET(ufd, &afds);
    434 
    435 	nfds = MAX(sfd + 1, ufd + 1);
    436 
    437 	/* accept connections until killed */
    438 	while (1) {
    439 		socklen_t fromlen;
    440 
    441 		memcpy(&rfds, &afds, sizeof(rfds));
    442 
    443 		if (select(nfds, &rfds, NULL, NULL,
    444 			   NULL) < 0) {
    445 			if (errno != EINTR) {
    446 				perror("server select");
    447 				exit(1);
    448 			}
    449 			continue;
    450 		}
    451 		if (FD_ISSET(sfd, &rfds)) {
    452 			int newfd;
    453 
    454 			fromlen = sizeof(fsin);
    455 			newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen);
    456 			if (newfd >= 0) {
    457 				FD_SET(newfd, &afds);
    458 				nfds = MAX(nfds, newfd + 1);
    459 				/* send something back */
    460 				(void)write(newfd, "hoser\n", 6);
    461 			}
    462 		}
    463 		if (FD_ISSET(ufd, &rfds)) {
    464 			int newfd;
    465 
    466 			fromlen = sizeof(fsun);
    467 			newfd = accept(ufd, (struct sockaddr *)&fsun, &fromlen);
    468 			if (newfd >= 0) {
    469 				FD_SET(newfd, &afds);
    470 				nfds = MAX(nfds, newfd + 1);
    471 			}
    472 		}
    473 		for (fd = 0; fd < nfds; ++fd)
    474 			if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) {
    475 				char rbuf[1024];
    476 
    477 				cc = read(fd, rbuf, sizeof(rbuf));
    478 				if (cc && rbuf[0] == 'R')
    479 					sender(fd);
    480 				if (cc == 0 || (cc < 0 && errno != EINTR)) {
    481 					(void)close(fd);
    482 					FD_CLR(fd, &afds);
    483 				}
    484 			}
    485 	}
    486 }
    487 
    488 #define TM	"from recvmsg01 server"
    489 
    490 /* special for rights-passing test */
    491 void sender(int fd)
    492 {
    493 	struct msghdr mh;
    494 	struct cmsghdr *control;
    495 	char tmpfn[1024], snd_cbuf[1024];
    496 	int tfd;
    497 
    498 	(void)strcpy(tmpfn, "smtXXXXXX");
    499 	tfd = mkstemp(tmpfn);
    500 	if (tfd < 0)
    501 		return;
    502 
    503 	memset(&mh, 0x00, sizeof(mh));
    504 
    505 	/* set up cmsghdr */
    506 	control = (struct cmsghdr *)snd_cbuf;
    507 	memset(control, 0x00, sizeof(struct cmsghdr));
    508 	control->cmsg_len = sizeof(struct cmsghdr) + 4;
    509 	control->cmsg_level = SOL_SOCKET;
    510 	control->cmsg_type = SCM_RIGHTS;
    511 	*(int *)CMSG_DATA(control) = tfd;
    512 
    513 	/* set up msghdr */
    514 	iov[0].iov_base = TM;
    515 	iov[0].iov_len = sizeof(TM);
    516 	mh.msg_iov = iov;
    517 	mh.msg_iovlen = 1;
    518 	mh.msg_flags = 0;
    519 	mh.msg_control = control;
    520 	mh.msg_controllen = control->cmsg_len;
    521 
    522 	/* do it */
    523 	(void)sendmsg(fd, &mh, 0);
    524 	(void)close(tfd);
    525 	(void)unlink(tmpfn);
    526 }
    527