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