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
main(int argc,char * argv[])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
setup(void)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
cleanup(void)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
setup0(void)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
cleanup0(void)287 void cleanup0(void)
288 {
289 s = -1;
290 }
291
setup1(void)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
setup2(void)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
setup3(void)329 void setup3(void)
330 {
331 setup2();
332 controllen = sizeof(struct cmsghdr) - 1;
333 }
334
setup4(void)335 void setup4(void)
336 {
337 setup2();
338 controllen = 128 * 1024;
339 }
340
cleanup1(void)341 void cleanup1(void)
342 {
343 (void)close(s);
344 close(ufd);
345 close(sfd);
346 s = -1;
347 }
348
cleanup2(void)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
start_server(struct sockaddr_in * ssin,struct sockaddr_un * ssun)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
do_child(void)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 */
sender(int fd)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