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