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