recvmsg01.c revision d7aa17621d042bffa2bee6447b422ca53877dfa7
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 "usctest.h" 60#include "msg_common.h" 61 62char *TCID = "recvmsg01"; 63int testno; 64 65char buf[1024], cbuf[1024]; 66int s; /* socket descriptor */ 67int passed_fd = -1; /* rights-passing test descriptor */ 68struct sockaddr_in sin1, from; 69struct sockaddr_un sun1; 70struct msghdr msgdat; 71struct cmsghdr *control = 0; 72int controllen = 0; 73struct iovec iov[1]; 74static int sfd; /* shared between do_child and start_server */ 75static int ufd; /* shared between do_child and start_server */ 76 77void setup(void); 78void setup0(void); 79void setup1(void); 80void setup2(void); 81void setup3(void); 82void setup4(void); 83void cleanup(void); 84void cleanup0(void); 85void cleanup1(void); 86void cleanup2(void); 87void do_child(void); 88 89void sender(int); 90pid_t start_server(struct sockaddr_in *, struct sockaddr_un *); 91 92struct 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 184int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]); 185 186int exp_enos[] = { EBADF, ENOTSOCK, EFAULT, EINVAL, 0 }; 187 188#ifdef UCLINUX 189static char *argv0; 190#endif 191 192int main(int argc, char *argv[]) 193{ 194 int lc; 195 const char *msg; 196 197 if ((msg = parse_opts(argc, argv, NULL, NULL)) != NULL) 198 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); 199#ifdef UCLINUX 200 argv0 = argv[0]; 201 maybe_run_child(&do_child, "dd", &sfd, &ufd); 202#endif 203 204 setup(); 205 206 TEST_EXP_ENOS(exp_enos); 207 208 for (lc = 0; TEST_LOOPING(lc); ++lc) { 209 tst_count = 0; 210 for (testno = 0; testno < TST_TOTAL; ++testno) { 211 tdat[testno].setup(); 212 213 /* setup common to all tests */ 214 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 TEST_ERROR_LOG(TEST_ERRNO); 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 249pid_t pid; 250char tmpsunpath[1024]; 251 252void 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 270void cleanup(void) 271{ 272 if (pid > 0) 273 (void)kill(pid, SIGKILL); /* kill server */ 274 if (tmpsunpath[0] != '\0') 275 (void)unlink(tmpsunpath); 276 TEST_CLEANUP; 277 tst_rmdir(); 278 279} 280 281void setup0(void) 282{ 283 if (tdat[testno].experrno == EBADF) 284 s = 400; /* anything not an open file */ 285 else if ((s = open("/dev/null", O_WRONLY)) == -1) 286 tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed"); 287} 288 289void cleanup0(void) 290{ 291 s = -1; 292} 293 294void setup1(void) 295{ 296 fd_set rdfds; 297 struct timeval timeout; 298 int n; 299 300 s = socket(tdat[testno].domain, tdat[testno].type, tdat[testno].proto); 301 if (s < 0) 302 tst_brkm(TBROK | TERRNO, cleanup, "socket setup failed"); 303 if (tdat[testno].type == SOCK_STREAM) { 304 if (tdat[testno].domain == PF_INET) { 305 if (connect(s, (struct sockaddr *)&sin1, sizeof(sin1)) < 306 0) 307 tst_brkm(TBROK | TERRNO, cleanup, 308 "connect failed"); 309 /* Wait for something to be readable, else we won't detect EFAULT on recv */ 310 FD_ZERO(&rdfds); 311 FD_SET(s, &rdfds); 312 timeout.tv_sec = 2; 313 timeout.tv_usec = 0; 314 n = select(s + 1, &rdfds, 0, 0, &timeout); 315 if (n != 1 || !FD_ISSET(s, &rdfds)) 316 tst_brkm(TBROK, cleanup, 317 "client setup1 failed - no message ready in 2 sec"); 318 } else if (tdat[testno].domain == PF_UNIX) { 319 if (connect(s, (struct sockaddr *)&sun1, sizeof(sun1)) < 320 0) 321 tst_brkm(TBROK | TERRNO, cleanup, 322 "UD connect failed"); 323 } 324 } 325} 326 327void setup2(void) 328{ 329 setup1(); 330 if (write(s, "R", 1) < 0) 331 tst_brkm(TBROK | TERRNO, cleanup, "test setup failed: write:"); 332 control = (struct cmsghdr *)cbuf; 333 controllen = control->cmsg_len = sizeof(cbuf); 334} 335 336void setup3(void) 337{ 338 setup2(); 339 controllen = sizeof(struct cmsghdr) - 1; 340} 341 342void setup4(void) 343{ 344 setup2(); 345 controllen = 128 * 1024; 346} 347 348void cleanup1(void) 349{ 350 (void)close(s); 351 close(ufd); 352 close(sfd); 353 s = -1; 354} 355 356void cleanup2(void) 357{ 358 close(ufd); 359 close(sfd); 360 (void)close(s); 361 s = -1; 362 363 if (passed_fd >= 0) 364 (void)close(passed_fd); 365 passed_fd = -1; 366 control = 0; 367 controllen = 0; 368} 369 370pid_t start_server(struct sockaddr_in *ssin, struct sockaddr_un *ssun) 371{ 372 pid_t pid; 373 socklen_t slen = sizeof(*ssin); 374 375 ssin->sin_family = AF_INET; 376 ssin->sin_port = 0; /* pick random free port */ 377 ssin->sin_addr.s_addr = INADDR_ANY; 378 379 /* set up inet socket */ 380 sfd = socket(PF_INET, SOCK_STREAM, 0); 381 if (sfd < 0) { 382 tst_brkm(TBROK | TERRNO, cleanup, "server socket failed"); 383 return -1; 384 } 385 if (bind(sfd, (struct sockaddr *)ssin, sizeof(*ssin)) < 0) { 386 tst_brkm(TBROK | TERRNO, cleanup, "server bind failed"); 387 return -1; 388 } 389 if (listen(sfd, 10) < 0) { 390 tst_brkm(TBROK | TERRNO, cleanup, "server listen failed"); 391 return -1; 392 } 393 if (getsockname(sfd, (struct sockaddr *)ssin, &slen) == -1) 394 tst_brkm(TBROK | TERRNO, cleanup, "getsockname failed"); 395 396 /* set up UNIX-domain socket */ 397 ufd = socket(PF_UNIX, SOCK_STREAM, 0); 398 if (ufd < 0) { 399 tst_brkm(TBROK | TERRNO, cleanup, "server UD socket failed"); 400 return -1; 401 } 402 if (bind(ufd, (struct sockaddr *)ssun, sizeof(*ssun))) { 403 tst_brkm(TBROK | TERRNO, cleanup, "server UD bind failed"); 404 return -1; 405 } 406 if (listen(ufd, 10) < 0) { 407 tst_brkm(TBROK | TERRNO, cleanup, "server UD listen failed"); 408 return -1; 409 } 410 411 switch ((pid = FORK_OR_VFORK())) { 412 case 0: /* child */ 413#ifdef UCLINUX 414 if (self_exec(argv0, "dd", sfd, ufd) < 0) 415 tst_brkm(TBROK | TERRNO, cleanup, 416 "server self_exec failed"); 417#else 418 do_child(); 419#endif 420 break; 421 case -1: 422 tst_brkm(TBROK | TERRNO, cleanup, "server fork failed"); 423 /* fall through */ 424 default: /* parent */ 425 (void)close(sfd); 426 (void)close(ufd); 427 return pid; 428 } 429 exit(1); 430} 431 432void do_child(void) 433{ 434 struct sockaddr_in fsin; 435 struct sockaddr_un fsun; 436 fd_set afds, rfds; 437 int nfds, cc, fd; 438 439 FD_ZERO(&afds); 440 FD_SET(sfd, &afds); 441 FD_SET(ufd, &afds); 442 443 nfds = MAX(sfd + 1, ufd + 1); 444 445 /* accept connections until killed */ 446 while (1) { 447 socklen_t fromlen; 448 449 memcpy(&rfds, &afds, sizeof(rfds)); 450 451 if (select(nfds, &rfds, NULL, NULL, 452 NULL) < 0) { 453 if (errno != EINTR) { 454 perror("server select"); 455 exit(1); 456 } 457 continue; 458 } 459 if (FD_ISSET(sfd, &rfds)) { 460 int newfd; 461 462 fromlen = sizeof(fsin); 463 newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen); 464 if (newfd >= 0) { 465 FD_SET(newfd, &afds); 466 nfds = MAX(nfds, newfd + 1); 467 /* send something back */ 468 (void)write(newfd, "hoser\n", 6); 469 } 470 } 471 if (FD_ISSET(ufd, &rfds)) { 472 int newfd; 473 474 fromlen = sizeof(fsun); 475 newfd = accept(ufd, (struct sockaddr *)&fsun, &fromlen); 476 if (newfd >= 0) 477 FD_SET(newfd, &afds); 478 } 479 for (fd = 0; fd < nfds; ++fd) 480 if (fd != sfd && fd != ufd && FD_ISSET(fd, &rfds)) { 481 char rbuf[1024]; 482 483 cc = read(fd, rbuf, sizeof(rbuf)); 484 if (cc && rbuf[0] == 'R') 485 sender(fd); 486 if (cc == 0 || (cc < 0 && errno != EINTR)) { 487 (void)close(fd); 488 FD_CLR(fd, &afds); 489 } 490 } 491 } 492} 493 494#define TM "from recvmsg01 server" 495 496/* special for rights-passing test */ 497void sender(int fd) 498{ 499 struct msghdr mh; 500 struct cmsghdr *control; 501 char tmpfn[1024], snd_cbuf[1024]; 502 int tfd; 503 504 (void)strcpy(tmpfn, "smtXXXXXX"); 505 tfd = mkstemp(tmpfn); 506 if (tfd < 0) 507 return; 508 509 memset(&mh, 0x00, sizeof(mh)); 510 511 /* set up cmsghdr */ 512 control = (struct cmsghdr *)snd_cbuf; 513 memset(control, 0x00, sizeof(struct cmsghdr)); 514 control->cmsg_len = sizeof(struct cmsghdr) + 4; 515 control->cmsg_level = SOL_SOCKET; 516 control->cmsg_type = SCM_RIGHTS; 517 *(int *)CMSG_DATA(control) = tfd; 518 519 /* set up msghdr */ 520 iov[0].iov_base = TM; 521 iov[0].iov_len = sizeof(TM); 522 mh.msg_iov = iov; 523 mh.msg_iovlen = 1; 524 mh.msg_flags = 0; 525 mh.msg_control = control; 526 mh.msg_controllen = control->cmsg_len; 527 528 /* do it */ 529 (void)sendmsg(fd, &mh, 0); 530 (void)close(tfd); 531 (void)unlink(tmpfn); 532} 533